package org.jsola.contract.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.timevale.esign.sdk.tech.bean.result.FileDigestSignResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.http.entity.ContentType;
import org.jsola.cache.ICache;
import org.jsola.common.BeanKit;
import org.jsola.common.CollectionKit;
import org.jsola.common.DateKit;
import org.jsola.common.StrKit;
import org.jsola.contract.common.CacheKeyKit;
import org.jsola.contract.common.FileHelper;
import org.jsola.contract.common.PdfUtil;
import org.jsola.contract.common.WkPdfHelper;
import org.jsola.contract.constant.*;
import org.jsola.contract.core.ContractProperties;
import org.jsola.contract.core.MockMultipartFile;
import org.jsola.contract.dao.IPersonSignatureDAO;
import org.jsola.contract.dto.*;
import org.jsola.contract.entity.*;
import org.jsola.contract.exception.ContractException;
import org.jsola.contract.provider.ILogProviderService;
import org.jsola.contract.provider.IMessageProviderService;
import org.jsola.contract.provider.ISiteConfigProviderService;
import org.jsola.contract.provider.IUserProviderService;
import org.jsola.contract.query.SealPositionQuery;
import org.jsola.contract.query.VariableQuery;
import org.jsola.contract.service.*;
import org.jsola.contract.vo.*;
import org.jsola.core.Result;
import org.jsola.exception.ParamException;
import org.jsola.file.constant.FileConstants;
import org.jsola.file.core.FileProperties;
import org.jsola.file.entity.FileVersionDO;
import org.jsola.file.service.ISiteFileService;
import org.jsola.log.constant.ActionType;
import org.jsola.message.dto.ShortMessageDTO;
import org.jsola.message.dto.WeiXinDTO;
import org.jsola.message.dto.WxMiNiParam;
import org.jsola.message.dto.WxTemplateParam;
import org.jsola.user.core.TokenUser;
import org.jsola.user.entity.UnionOpenIdDO;
import org.jsola.user.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.HtmlUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.jsola.contract.constant.ContractConstants.*;
import static org.jsola.user.constant.UserConstants.DEFAULT_SITE_ID;

/**
 * @author JanusMix
 */
@Slf4j
@Service("contractSignService")
public class SignServiceImpl implements ISignService {

    @Autowired
    private IContractService contractService;
    @Autowired
    private ICategoryService categoryService;
    @Autowired
    private IPartySignService partySignService;
    @Autowired
    private ISealPositionService sealPositionService;
    @Autowired
    private IVariableService variableService;
    @Autowired
    private IContractVariableService contractVariableService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private ISealService sealService;
    @Autowired
    private IThirdSignService thirdSignService;
    @Autowired
    private IAppendixService appendixService;
    @Autowired
    private FileProperties fileProperties;
    @Autowired
    private ISiteFileService siteFileService;
    @Autowired
    private ContractProperties contractProperties;

    @Autowired
    private IUserProviderService userProviderService;

    @Autowired
    private IMessageProviderService messageProviderService;

    @Autowired
    private IPersonSignatureService personSignatureService;

    @Autowired
    private ICompanyContractShowService companyContractShowService;

    @Value("${spring.application.name}")
    private String appName;

    @Autowired
    private ICache cache;

    @Autowired
    private List<IContractSignCallbackService> contractSignCallbackServiceList;

    @Autowired
    private IContractPathService contractPathService;

    @Autowired
    private ISiteConfigProviderService siteConfigProviderService;

    @Autowired
    private ITemplateService templateService;

    @Autowired
    private IContractConfigSiteService contractConfigSiteService;

    @Autowired
    private IPersonSignatureDAO personSignatureDao;


    /**
     * 最大支持签署方
     */
    private static final Integer PARTY_MAX = 2;

    /**
     * a标签正则
     */
    private static final String REG_A = "<a[^>]*?>";
    private static final Pattern A_REG_PATTERN;

    static {
        A_REG_PATTERN = Pattern.compile(REG_A, Pattern.CASE_INSENSITIVE);
    }


    @Override
    public Result<Boolean> sendCode(TokenUser tokenUser) {
        String userId = tokenUser.getUserId();
        AccountDO accountDO = accountService.findAccount(AccountType.PERSON, userId);
        ParamException.notNull(accountDO, "用户未实名");
        ShortMessageDTO shortMessageDTO = new ShortMessageDTO();
        shortMessageDTO.setAppName(appName);
        shortMessageDTO.setPhone(accountDO.getMobile());
        shortMessageDTO.setThirdId(contractProperties.getSmsTemplateId());
        shortMessageDTO.setScene(SCENE);
        return Result.success(
                messageProviderService.sendShortMessage(shortMessageDTO, tokenUser)
        );
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public List<ContractVO> publishTemplateSign(PublishSignDTO publishSignDTO, TokenUser tokenUser) {
        // 查看缓存中的余量
        String contractMargin = CacheKeyKit.getContractMargin(tokenUser.getSiteId());
        ContractMarginVO contractMarginVO = cache.get(contractMargin);
        ContractConfigSiteDO contractConfigSiteDO = null;
        // 若没有 查询表中的余量
        if (contractMarginVO == null) {
            contractConfigSiteDO = contractConfigSiteService.selectBySiteId(tokenUser.getSiteId());
            // 未查到合同余量 直接通过
            ParamException.notNull(contractConfigSiteDO, "当前电子合同余额不足，请联系管理员进行充值");
//            if (contractConfigSiteDO == null) {
//                return publishTemplateSign(publishSignDTO, Boolean.FALSE, tokenUser);
//            }
            contractMarginVO = contractConfigSiteDO.to(ContractMarginVO.class);
            contractMarginVO.setDatabaseModified(new Date());
        }
        ParamException.isTrue(contractMarginVO.getEnable(), "未启用合同配置，请联系管理员");
        // 累计签署份数
        Integer totalCount = contractMarginVO.getTotalCount();
        // 剩余额度
        Integer quotaRemain = contractMarginVO.getQuotaRemain();
        // 本次签署量
        int size = publishSignDTO.getMemberVariableList().size();
        // 合同 提示：
        ParamException.isTrue(quotaRemain >= size, "当前电子合同余额不足，请联系管理员进行充值");
        totalCount += size;
        quotaRemain -= size;
        contractMarginVO.setTotalCount(totalCount);
        contractMarginVO.setQuotaRemain(quotaRemain);
        int oneDay = 1000 * 60 * 60 * 24;
        // 余额为0 或 超过一天 更新到数据库
        if (contractMarginVO.getQuotaRemain() == 0 || System.currentTimeMillis()
                - contractMarginVO.getDatabaseModified().getTime() > oneDay) {
            if (contractConfigSiteDO == null) {
                contractConfigSiteDO = contractConfigSiteService.selectBySiteId(tokenUser.getSiteId());
                // 数据库中已删除 删除缓存中的余量
                if (contractConfigSiteDO == null) {
                    cache.delKey(contractMargin);
                    return publishTemplateSign(publishSignDTO, Boolean.FALSE, tokenUser);
                }
            }
            contractConfigSiteDO.setTotalCount(totalCount);
            contractConfigSiteDO.setQuotaRemain(quotaRemain);
            contractConfigSiteService.updateByIdSelective(contractConfigSiteDO, contractConfigSiteDO.getSiteId(), tokenUser.getUserId());
            contractMarginVO.setDatabaseModified(new Date());
        }
        // 减少1后放入缓存
        cache.set(contractMargin, contractMarginVO);
        return publishTemplateSign(publishSignDTO, Boolean.FALSE, tokenUser);
    }

    @Override
    public List<ContractVO> publishTemplateSignSingle(PublishSignSigngleDTO publishSignSigngleDTO, boolean showDiffSite, TokenUser tokenUser) {
        return publishTemplateSignSignle(publishSignSigngleDTO, showDiffSite, tokenUser);
    }

    @Override
    public List<String> modifyContract(List<ContractDO> contractDOList, Long sealId, TokenUser tokenUser) {
        // 印章信息
        SealVO sealVO = sealService.selectById(sealId, tokenUser.getSiteId());
        ParamException.notNull(sealVO, "印章信息有误，请刷新后重试");
        // 转换后的文件
        List<MultipartFile> multipartFileList = new ArrayList<>();
        for (ContractDO contractDO : contractDOList) {
            if (contractDO.getName().contains("员工手册")) {
                continue;
            }
            // 个人签名
            PersonSignatureDO personSignatureDO = personSignatureService.findByContractId(contractDO.getId(), tokenUser.getSiteId());
            ParamException.notNull(personSignatureDO, "个人未签署无法执行");
            //保存合同pdf文件(空文件)
            String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
            String filePath = fileProperties.getTempPath() + File.separator + fileName;
            FileHelper.saveFileByStream(new byte[]{}, filePath);

            String templateContent = contractDO.getContent();
            String contractVariable = CacheKeyKit.getContractVariable();
            List<ContractVariableDTO> contractVariables = cache.get(contractVariable);
            if (CollectionUtils.isEmpty(contractVariables)) {
                contractVariables = new ArrayList<>();
                ContractVariableDTO contractVariableDTO1 = new ContractVariableDTO();
                contractVariableDTO1.setBeforeStr("山东华杰人力资源管理有限公司");
                contractVariableDTO1.setAfterStr("山东华杰人力资源管理有限公司青岛第一分公司");
                contractVariables.add(contractVariableDTO1);

                ContractVariableDTO contractVariableDTO2 = new ContractVariableDTO();
                contractVariableDTO2.setBeforeStr("济南市历下区解放路</font>22<font face=\"宋体\">号燕山大厦4楼");
                contractVariableDTO2.setAfterStr("山东省青岛市莱西市经济开发区烟台南路88号甲-17");
                contractVariables.add(contractVariableDTO2);

                ContractVariableDTO contractVariableDTO3 = new ContractVariableDTO();
                contractVariableDTO3.setBeforeStr("济南市解放路</font>22<font face=\"宋体\">号燕山大厦四楼");
                contractVariableDTO3.setAfterStr("山东省青岛市莱西市经济开发区烟台南路88号甲-17");
                contractVariables.add(contractVariableDTO3);

                ContractVariableDTO contractVariableDTO4 = new ContractVariableDTO();
                contractVariableDTO4.setBeforeStr("法人（主要负责人）：刘鑫");
                contractVariableDTO4.setAfterStr("法人（主要负责人）：班雪");
                contractVariables.add(contractVariableDTO4);

                ContractVariableDTO contractVariableDTO5 = new ContractVariableDTO();
                contractVariableDTO5.setBeforeStr("法定代表人：刘鑫");
                contractVariableDTO5.setAfterStr("法定代表人：班雪");
                contractVariables.add(contractVariableDTO5);
                cache.set(contractVariable, contractVariables);
                throw new ParamException("未查询到需要替换的合同内容，请点击重试");
            }
            // 更换公司名称
            templateContent = templateContent.replaceAll(contractDO.getPartyAName(), "山东华杰人力资源管理有限公司青岛第一分公司");
            for (ContractVariableDTO contractVariableDTO : contractVariables) {
                // 替换合同信息 用缓存的内容替换
                templateContent = templateContent.replaceAll(contractVariableDTO.getBeforeStr(), contractVariableDTO.getAfterStr());
            }
            WkPdfHelper.exportPdf(filePath, templateContent);
            byte[] bytes = FileHelper.getFileBytes(filePath);

            // 当时个人签署时的人
            TokenUser tokenUserPerSon = new TokenUser();
            tokenUserPerSon.setSiteId(tokenUser.getSiteId());
            tokenUserPerSon.setUserId(personSignatureDO.getUserId());

            //获取签署人账户对象
            AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUserPerSon.getUserId());
            ParamException.notNull(accountDO, "用户未实名");
            ParamException.isTrue(accountDO.getReal(), "用户未实名");


            SealPositionQuery sealPositionQuery = new SealPositionQuery();
            if (contractDO.getTemplateId() != null) {
                sealPositionQuery.setTemplateId(contractDO.getTemplateId());
            }
            sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
            List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
            List<SealPositionDO> perSonPositionList = new ArrayList<>();
            List<SealPositionDO> companyPositionList = new ArrayList<>();
            // 获取公司签署位置
            for (SealPositionDO sealPositionDO : sealPositionList) {
                if (AccountType.PERSON.getValue().equals(sealPositionDO.getAccountType())) {
                    // 获取个人签署位置
                    perSonPositionList.add(sealPositionDO);
                } else if (AccountType.ENTERPRISE.getValue().equals(sealPositionDO.getAccountType())) {
                    // 获取公司签署位置
                    companyPositionList.add(sealPositionDO);
                }
            }
            // 替换个人签名
            byte[] resultStream;
            FileDigestSignResult perSonResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                    , personSignatureDO.getSeal(), bytes, perSonPositionList, SealType.SINGLE, tokenUser);
            if (CollectionUtils.isEmpty(companyPositionList)) {
                resultStream = perSonResult.getStream();
            } else {
                // 替换公司签名
                FileDigestSignResult companyResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                        , sealVO.getSealString(), perSonResult.getStream(), companyPositionList, SealType.SINGLE, tokenUser);
                WkPdfHelper.exportPdf(filePath, templateContent);
                resultStream = companyResult.getStream();
            }

            String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
            MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), resultStream);
            multipartFileList.add(file);
        }
        return siteFileService.uploadDiffSite(multipartFileList.toArray(new MultipartFile[0])
                , FileConstants.DOWNLOAD_TYPE_INLINE, tokenUser);
    }


    public void createContract(Long contractId) throws Exception {
        TokenUser tokenUser = new TokenUser();
        tokenUser.setUserId("7A2A17F5AE2F429AA553D4ACFF0E4C66");
        tokenUser.setSiteId("EAE7A6E4767345B284C2A443D729E2F0");


        // 替换个人签名
        String contentId = "D0C8479A297441C29AC9D5C3B896F593";
        String filePath = "D:\\tmp.pdf";
        byte[] bytes = FileHelper.getFileBytes(filePath);


        String accountId = "D0C8479A297441C29AC9D5C3B896F593";
        // 印章base64 公司保存：sealVO.getSealString() 个人：personSignatureDO.getSeal()

        // 印章位置
        List<SealPositionDO> personPsitions = new ArrayList<>();
        SealPositionDO person = new SealPositionDO();
        person.setSealPositionX("167.99999999999997");
        person.setSealPositionY("290.4913494809689");
        person.setSealPage(25);
        personPsitions.add(person);

        PersonSignatureDO personSignatureDO = new PersonSignatureDO();
        personSignatureDO.setContractId(contractId);
        String personSealData = personSignatureDao.selectOne(personSignatureDO).getSeal();

        // 个人签署
        FileDigestSignResult perSonResult = thirdSignService.localSafeSignPDF(accountId
                , personSealData, bytes, personPsitions, SealType.SINGLE, tokenUser);
        accountId = "5406B7C6269D4B66A1FEF16DFDA5010E";


        // 替换公司签名
        List<SealPositionDO> companyPsitions = new ArrayList<>();
        SealPositionDO company = new SealPositionDO();
        company.setSealPositionX("168.00000000000003");
        company.setSealPositionY("424.7750865051903");
        company.setSealPage(25);
        companyPsitions.add(company);

        //查找甲方章
        ContractVO contractVO = contractService.selectById(contractId, tokenUser.getSiteId());
        CategoryVO categoryvO = categoryService.selectById(contractVO.getCategoryId(), tokenUser.getSiteId());
        SealVO sealVO = sealService.selectById(categoryvO.getEnterpriseSealId(), tokenUser.getSiteId());

        FileDigestSignResult companyResult = thirdSignService.localSafeSignPDF(accountId
                , sealVO.getSealString(), perSonResult.getStream(), companyPsitions, SealType.SINGLE, tokenUser);
        byte[] resultStream = companyResult.getStream();
        File result = new File("D:\\result.pdf");
        FileOutputStream fileOutputStream = new FileOutputStream(result);
        fileOutputStream.write(resultStream);
        fileOutputStream.close();
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public List<ContractVO> publishTemplateSignDiffSite(PublishSignDTO publishSignDTO, TokenUser tokenUser) {
        return publishTemplateSign(publishSignDTO, Boolean.TRUE, tokenUser);
    }

    /**
     * 发起合同签署
     *
     * @param publishSignDTO 合同参数
     * @param showDiffSite   合同文件是否在不同站点显示
     * @param tokenUser      当前用户
     * @return 合同信息
     */
    private List<ContractVO> publishTemplateSign(PublishSignDTO publishSignDTO, Boolean showDiffSite, TokenUser tokenUser) {
        ParamException.hasText(publishSignDTO.getSubjectId(), "请传递业务id");
        long startTimeMillis = System.currentTimeMillis();
        // 增加发起锁,防止同一时间点击多次
        String redisCode = CacheKeyKit.getCreateSignLockKey(publishSignDTO.getSubjectId());
        List<ContractVO> contractVOList = new ArrayList<>();
        try {
            if (Objects.nonNull(cache.get(redisCode))) {
                // 有正在发起的了
                throw new ParamException("本公司有正在发起的合同,请稍候发起");
            } else {
                // 锁5分钟
                cache.set(redisCode, redisCode, 5 * 60 * 1000L);
            }
            Long categoryId = publishSignDTO.getCategoryId();
            Long templateId = publishSignDTO.getTemplateId();
            boolean isCheckFace = publishSignDTO.getIsCheckFace() == null ?
                    false : publishSignDTO.getIsCheckFace();
            String siteId = tokenUser.getSiteId();
            //查询合同类别
            CategoryDO categoryDO = categoryService.selectDOById(publishSignDTO.getCategoryId(), tokenUser.getSiteId());
            ParamException.notNull(categoryDO, "合同分类不存在");
            for (MemberVariableDTO memberVariableDTO : publishSignDTO.getMemberVariableList()) {
                memberVariableDTO.setContractName(categoryDO.getName() + "-" + memberVariableDTO.getPartyBName());
            }
            //查询企业账户id
            String enterprisePartyAccountId = "";
            if (SignMethod.BOTH.getValue().equals(categoryDO.getSignType()) || SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                SealDO sealDO = sealService.selectDOById(categoryDO.getEnterpriseSealId(), siteId);
                if (sealDO == null || sealDO.getAccountId() == null) {
                    throw new ParamException("企业未认证");
                }
                enterprisePartyAccountId = sealDO.getAccountId();
            }
            //查询模板签章位置
            SealPositionQuery sealPositionQuery = new SealPositionQuery();
            sealPositionQuery.setTemplateId(templateId);
            sealPositionQuery.setType(ContractConstants.SEAL_POSITION_TYPE_REGULAR);
            List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(sealPositionList)) {
                throw new ParamException("请先设置模板默认印章位置");
            }
            SealPositionDO enterpriseSealPosition = new SealPositionDO();
            SealPositionDO personSealPosition = new SealPositionDO();
            if (!SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
                enterpriseSealPosition = sealPositionList.stream()
                        .filter(sealPositionDO -> AccountType.ENTERPRISE.getValue().equals(sealPositionDO.getAccountType()))
                        .filter(sealPositionDO -> ContractConstants.SEAL_POSITION_TYPE_REGULAR.equals(sealPositionDO.getType()))
                        .findFirst().orElseThrow(() -> new ParamException("未设置企业默认印章位置"));
            }
            if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                personSealPosition = sealPositionList.stream()
                        .filter(sealPositionDO -> AccountType.PERSON.getValue().equals(sealPositionDO.getAccountType()))
                        .filter(sealPositionDO -> ContractConstants.SEAL_POSITION_TYPE_REGULAR.equals(sealPositionDO.getType()))
                        .findFirst().orElseThrow(() -> new ParamException("未设置个人默认印章位置"));
            }
            List<MemberVariableDTO> memberVariableList = publishSignDTO.getMemberVariableList();
            List<PartySignAddDTO> partySignList = new ArrayList<>();
            List<ContractVariableAddDTO> contractVariableList = new ArrayList<>();


            MultipartFile[] multipartFiles = new MultipartFile[memberVariableList.size()];
            List<Long> contractIdList = new ArrayList<>();

            // 合同签署发送微信通告
            List<SignWeiXinSendDTO> weiXinSendList = new ArrayList<>();

            log.info("发起合同签署，校验数据啊用时：{}毫秒", System.currentTimeMillis() - startTimeMillis);
            String partyBAccountId = "";
            TemplateDO templateDO = templateService.selectDOById(publishSignDTO.getTemplateId(), tokenUser.getSiteId());
            ParamException.notNull(templateDO, "合同模板不存在,请检查");
            ParamException.hasText(templateDO.getContent(), "合同内容为空,请检查");
            // 替换变量
            memberVariableList.parallelStream().forEach(memberVariableDTO -> {
                if (!StringUtils.isBlank(memberVariableDTO.getTemplateContent())) {
                    return;
                }
                // 替换合同中的变量
                String content = substitutionVariable(templateDO.getContent(), memberVariableDTO.getVariableList(), contractVariableList);
                memberVariableDTO.setTemplateContent(content);
            });

            //所有成员变量
            for (int i = 0; i < memberVariableList.size(); i++) {
                long replace = System.currentTimeMillis();
                MemberVariableDTO memberVariableDTO = memberVariableList.get(i);
                String replaceContent = memberVariableDTO.getTemplateContent();
                ParamException.hasText(replaceContent, "合同内容为空,请检查");
                String templateContent = HtmlUtils.htmlUnescape(replaceContent);
                Matcher contentMatcher = A_REG_PATTERN.matcher(templateContent);
                templateContent = contentMatcher.replaceAll("").replaceAll("</a>", "");
                log.info("发起合同签署，替换信息用时：{}毫秒", System.currentTimeMillis() - replace);
//                if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                //查询成员账户
                AccountDO accountDO = accountService.findAccount(AccountType.find(memberVariableDTO.getAccountType()), memberVariableDTO.getMemberId());
                if (accountDO == null) {
                    //没有先新建一个未认证的账户
                    accountDO = new AccountDO();
                    accountDO.setMemberId(memberVariableDTO.getMemberId());
                    accountDO.setType(AccountType.PERSON.getValue());
                    accountDO.setReal(Boolean.FALSE);
                    accountDO = accountService.save(accountDO, DEFAULT_SITE_ID, tokenUser.getUserId());
                }
                partyBAccountId = accountDO.getId();
//                }
                // 每个合同需要初始化
                ContractDO contractDO = new ContractDO();
                contractDO.setCategoryId(categoryId);
                contractDO.setTemplateId(templateId);
                contractDO.setSubjectId(publishSignDTO.getSubjectId());
                contractDO.setPublishDate(memberVariableDTO.getPublishDate());
                contractDO.setSignDate(new Date());
                contractDO.setName(categoryDO.getName() + "-" + memberVariableDTO.getPartyBName());
                contractDO.setContractType(ContractType.ONLINE.getValue());
                contractDO.setFileType(categoryDO.getFileType());
                contractDO.setContractNo(memberVariableDTO.getContractNo());
                contractDO.setContent(templateContent);
                // 甲乙方名称
                contractDO.setPartyAAccountId(accountDO.getAccountId());
                contractDO.setPartyAName(memberVariableDTO.getPartyAName());
                contractDO.setPartyBName(memberVariableDTO.getPartyBName());
                contractDO.setPartyBAccountId(partyBAccountId);
                contractDO.setPartyBPhone(memberVariableDTO.getPartyBPhone());
                // 合同开始时间结束时间
                contractDO.setEndDate(memberVariableDTO.getEndDate());
                contractDO.setDateline(memberVariableDTO.getDateline());
                contractDO.setFileType(memberVariableDTO.getContractType());
                // 是否需要人脸核身
                contractDO.setIsCheckFace(isCheckFace);
                // 小程序是否展示
                boolean appIsShow = true;
                // 公司id
                String companyId = contractDO.getSubjectId();
                // 获取公司的配置
                CompanyContractShowVO contractShow = companyContractShowService.findByCompanyId(companyId, tokenUser);
                if (Objects.nonNull(contractShow)) {
                    appIsShow = contractShow.getIsShow();
                }
                contractDO.setAppIsShow(appIsShow);

                // 签署编号,当前时间+4位随机吗
                // 字符串形当前时间
                String dateStr = DateKit.getDateStr(DateKit.getToday(), "yyyyMMddHHmm");
                // 6位随机数
                String randomNum = RandomStringUtils.random(6, false, true);
                contractDO.setSignNo(dateStr + randomNum);

                if (SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                    contractDO.setStatus(ContractConstants.CONTRACT_STATUS_ENTERPRISE);
                } else {
                    contractDO.setStatus(ContractConstants.CONTRACT_STATUS_PERSON);
                }

                // 是否需要重新发起
                boolean needInitAgain = false;
                if (StringUtils.isNotEmpty(templateContent)) {
                    // 有签署确认时间字段,需要重新发起
                    needInitAgain = templateContent.contains(SIGN_CONFIRM_DATE_REPLACE);
                }
                contractDO.setNeedInitAgain(needInitAgain);

                //保存合同表
                contractDO = contractService.save(contractDO, siteId, tokenUser.getUserId());
                log.info("发起合同方法：publishTemplateSign, 合同名称：{},合同Id:{}", contractDO.getName(), contractDO.getId());
                contractIdList.add(contractDO.getId());
                ContractVO contractVO = contractDO.to(ContractVO.class);
                contractVO.setMemberId(memberVariableDTO.getMemberId());
                contractVOList.add(contractVO);

                // 有员工签署的才发送
                if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                    SignWeiXinSendDTO signWeiXinSend = new SignWeiXinSendDTO();
                    signWeiXinSend.setContractId(contractDO.getId());
                    signWeiXinSend.setContractName(memberVariableDTO.getContractName());
                    signWeiXinSend.setContractTypeId(categoryId);
                    signWeiXinSend.setExpireDays(categoryDO.getExpireDays());
                    // 甲乙方名称
                    signWeiXinSend.setAName(memberVariableDTO.getPartyAName());
                    signWeiXinSend.setAUserId(memberVariableDTO.getMemberId());
                    signWeiXinSend.setBName(memberVariableDTO.getPartyBName());

                    weiXinSendList.add(signWeiXinSend);
                }
                long saveTime = System.currentTimeMillis();
                //保存合同pdf文件
                String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
                String filePath = fileProperties.getTempPath() + File.separator + fileName;
                FileHelper.saveFileByStream(new byte[]{}, filePath);
                log.info("发起合同签署，保存合同pdf文件用时：{}毫秒", System.currentTimeMillis() - saveTime);

                if (needInitAgain) {
                    // 需要重新发起
                    // 首次发起,替换合同签署确认时间为空时间串
                    //      格式对应 '<signDateReplace>'
                    //      格式对应 '                '
                    templateContent = templateContent.replaceAll(SIGN_CONFIRM_DATE_REPLACE, "                ");
                    // 再次发起,替换时间为当前时间
                }

                WkPdfHelper.exportPdf(filePath, templateContent);
                byte[] bytes = FileHelper.getFileBytes(filePath);

                String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
                MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), bytes);
                multipartFiles[i] = file;
                //保存企业签署方
                if (!SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
                    ParamException.notNull(enterprisePartyAccountId, "企业方id不能为空");
                    PartySignAddDTO enterprisePartySignAddDTO = new PartySignAddDTO();
                    enterprisePartySignAddDTO.setAccountId(enterprisePartyAccountId);
                    enterprisePartySignAddDTO.setContractId(contractDO.getId());
                    enterprisePartySignAddDTO.setSealPositionId(enterpriseSealPosition.getId());
                    enterprisePartySignAddDTO.setSealId(categoryDO.getEnterpriseSealId());
                    enterprisePartySignAddDTO.setStatus(SIGN_STATUS_READY);
                    partySignList.add(enterprisePartySignAddDTO);
                }
                //保存员工签署方
                if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                    PartySignAddDTO personalPartySignAddDTO = new PartySignAddDTO();
                    personalPartySignAddDTO.setAccountId(partyBAccountId);
                    personalPartySignAddDTO.setContractId(contractDO.getId());
                    personalPartySignAddDTO.setSealPositionId(personSealPosition.getId());
                    personalPartySignAddDTO.setStatus(SIGN_STATUS_READY);
                    partySignList.add(personalPartySignAddDTO);
                }
                // 仅企业签署也需要保存员工签署方,为了兼容以前的逻辑,这里单独为企业单方签署做一下判断
                if (SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                    PartySignAddDTO personalPartySignAddDTO = new PartySignAddDTO();
                    personalPartySignAddDTO.setAccountId(partyBAccountId);
                    personalPartySignAddDTO.setContractId(contractDO.getId());
                    personalPartySignAddDTO.setSealPositionId(personSealPosition.getId());
                    // 状态为签署完成
                    personalPartySignAddDTO.setStatus(SIGN_STATUS_FINISH);
                    partySignList.add(personalPartySignAddDTO);
                }
                for (ContractVariableAddDTO variableAddDTO : contractVariableList) {
                    // 放入保存后合同的Id
                    variableAddDTO.setContractId(contractDO.getId());
                }
            }
            // 合同Id去重
            List<Long> distinctIds = contractIdList.parallelStream().distinct().collect(Collectors.toList());
            long saveTime = System.currentTimeMillis();
            log.info("发起合同签署，数据库开始保存：{}毫秒", System.currentTimeMillis() - startTimeMillis);
            partySignService.batchSave(partySignList, tokenUser);
            contractVariableService.batchSave(contractVariableList, tokenUser);
            log.info("发起合同签署，数据库保存结束：{}毫秒", System.currentTimeMillis() - startTimeMillis);
            log.info("发起合同签署，数据库保存用时：{}毫秒", saveTime - startTimeMillis);
            // 换用异步上传OSS
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            singleThreadExecutor.execute(() -> {

                //上传合同文件到oss
                List<String> pathList;
                long ossTime = System.currentTimeMillis();
                TokenUser tokenUserSite = new TokenUser();
                tokenUserSite.setSiteId(tokenUser.getSiteId());
                tokenUserSite.setUserId(tokenUser.getUserId());
                if (showDiffSite) {
                    tokenUserSite.setSiteId("defaultSite");
                    // 不同站点中使用
                    pathList = siteFileService.uploadDiffSite(multipartFiles, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
                } else {
                    // 相同站点中使用
                    pathList = siteFileService.upload(multipartFiles, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
                }
                log.info("发起合同签署，上传合同pdf文件用时：{}毫秒", System.currentTimeMillis() - ossTime);
                //保存所有合同文件路径
                for (int i = 0; i < distinctIds.size(); i++) {
                    //把签署前的文件删除
                    AppendixDO originFile = appendixService.selectByContractId(distinctIds.get(i), tokenUser.getSiteId());
                    if (originFile != null) {
                        appendixService.deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), originFile.getId());
                    }
                    FileVersionDO fileVersionDO = siteFileService.download(Collections.singletonList(pathList.get(i)), null,
                            tokenUserSite);
                    ParamException.notNull(fileVersionDO, "文件不存在或已删除");
                    File pdf = new File(fileVersionDO.getTempFilePath());
                    // 生成jpg
                    String fileName = System.currentTimeMillis() + ".jpg";
                    String filePath = fileProperties.getTempPath() + File.separator + fileName;
                    File file = new File(filePath);
                    PdfUtil.pdf2multiImage(pdf.getAbsolutePath(), filePath, 80);
                    MultipartFile[] multipartFilesJpg = new MultipartFile[1];
                    try {
                        FileInputStream fileInputStreamTwo = new FileInputStream(file);
                        MultipartFile multipartFile = new MockMultipartFile("copy" + file.getName(), file.getName(),
                                ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStreamTwo);
                        multipartFilesJpg[0] = multipartFile;
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw new ParamException("文件转化失败!");
                    }
                    List<String> downloadReport = siteFileService.upload(multipartFilesJpg, "attachment", tokenUser);
                    ContractException.isFalse(CollectionUtils.isEmpty(downloadReport), "上传文件出错了,请稍后重试或联系管理员");
                    // 删除生成的图片
                    file.delete();
                    AppendixDO appendixDO = new AppendixDO();
                    appendixDO.setPath(pathList.get(i));
                    appendixDO.setName(downloadReport.get(0));
                    appendixDO.setContractId(distinctIds.get(i));
                    appendixDO.setCurrentPage(0);
                    appendixService.save(appendixDO, siteId, tokenUser.getUserId());
                }

                //合同发起回调函数
                this.contractPublishCallback(distinctIds, tokenUser);

                if (!CollectionUtils.isEmpty(weiXinSendList)) {
                    // 发送微信公众号模板推送消息
                    this.sendMessageCallback(weiXinSendList, publishSignDTO.getSubjectName(), tokenUser);
                }
                log.info("发起合同签署，执行结束用时：{}毫秒", System.currentTimeMillis() - startTimeMillis);
//                contractVO.setPdfFilePath(String.join(",", pathList));
                if (SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                    List<SignWeiXinSendDTO> sendList = new ArrayList<>();
                    for (ContractVO contract : contractVOList) {
                        // 如果是企业单方签署这里要要调确认签署接口,把合同签完
                        List<PartySignAccountVO> partySignAccount =
                                partySignService.selectAccountByContractId(contract.getId(), tokenUser.getSiteId());
                        // 找出企业方的accountId
                        List<PartySignAccountVO> aAccountList = partySignAccount.stream()
                                .filter(account -> 2 == account.getType()).collect(Collectors.toList());
                        // 找出员工方的accountId
                        List<PartySignAccountVO> bAccountList = partySignAccount.stream()
                                .filter(account -> 1 == account.getType()).collect(Collectors.toList());
                        // 调用企业确认签署
                        this.contractSignAgain(contract.getId(), aAccountList.get(0).getMemberId(), tokenUser);
                        appendixService.setJpgToAppendix(contract.getId(), tokenUser);
                        // 签署完成需要发送微信消息通知
                        SignWeiXinSendDTO signWeiXinSend = new SignWeiXinSendDTO();
                        signWeiXinSend.setContractId(contract.getId());
                        signWeiXinSend.setContractName(contract.getName());
                        // 甲乙方名称
                        signWeiXinSend.setAName(contract.getPartyAName());
                        signWeiXinSend.setAUserId(bAccountList.get(0).getMemberId());
                        signWeiXinSend.setBName(contract.getPartyBName());
                        sendList.add(signWeiXinSend);
                    }
                    this.sendMessageCallback(sendList, null, tokenUser);
                }
            });
            try {
                // 保存log日志
                ILogProviderService logProviderService = BeanKit.getBean(ILogProviderService.class);
                if (logProviderService == null) {
                    log.error("logProviderService is null");
                    return contractVOList;
                }
                String content;
                if (CollectionUtils.isEmpty(memberVariableList)) {
                    return contractVOList;
                }
                String partyBName = memberVariableList.get(0).getPartyBName();
                if (memberVariableList.size() == 1) {
                    content = partyBName + "由管理员 " + tokenUser.getUserName() + " 发起合同签署";
                } else {
                    content = partyBName + "等" + memberVariableList.size() + "人由管理员" + tokenUser.getUserName() + "发起合同签署";
                }
                logProviderService.log(content, ActionType.SAVE.getValue(), "Sign"
                        , publishSignDTO.getSubjectId(), publishSignDTO.getSubjectId(), tokenUser);
            } catch (Exception e) {
                log.error("contract保存日志出错了", e);
            }
        } catch (Exception e) {
            log.error("合同发起出错:" + e.getMessage(), e);
            throw new ParamException("发起合同出错,错误信息:" + e.getMessage());
        } finally {
            // 删除锁
            cache.delKey(redisCode);
        }
        return contractVOList;
    }

    /**
     * 发起合同签署（异步使用）
     *
     * @param publishSignSigngleDTO 合同参数
     * @param showDiffSite          合同文件是否在不同站点显示
     * @param tokenUser             当前用户
     * @return 合同信息
     */
    private List<ContractVO> publishTemplateSignSignle(PublishSignSigngleDTO publishSignSigngleDTO, Boolean showDiffSite, TokenUser tokenUser) {
        StopWatch stopWatch = new StopWatch("发起合同签署（异步使用）");
        stopWatch.start("发起合同签署，校验数据");

        ParamException.hasText(publishSignSigngleDTO.getSubjectId(), "请传递业务id");
        List<ContractVO> contractVOList = new ArrayList<>();
        long startTimeMillis = System.currentTimeMillis();
        Long templateId = publishSignSigngleDTO.getTemplateId();
        boolean isCheckFace = publishSignSigngleDTO.getIsCheckFace() != null && publishSignSigngleDTO.getIsCheckFace();
        String siteId = tokenUser.getSiteId();
        //查询合同类别
        CategoryDO categoryDO = categoryService.selectDOById(publishSignSigngleDTO.getCategoryId(), tokenUser.getSiteId());
        ParamException.notNull(categoryDO, "合同分类不存在");
        //查询企业账户id
        String enterprisePartyAccountId = "";
        if (SignMethod.BOTH.getValue().equals(categoryDO.getSignType()) || SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
            SealDO sealDO = sealService.selectDOById(categoryDO.getEnterpriseSealId(), siteId);
            if (sealDO == null || sealDO.getAccountId() == null) {
                throw new ParamException("企业未认证");
            }
            enterprisePartyAccountId = sealDO.getAccountId();
        }
        //查询模板签章位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        sealPositionQuery.setTemplateId(templateId);
        sealPositionQuery.setType(ContractConstants.SEAL_POSITION_TYPE_REGULAR);
        List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(sealPositionList)) {
            throw new ParamException("请先设置模板默认印章位置");
        }
        SealPositionDO enterpriseSealPosition = new SealPositionDO();
        SealPositionDO personSealPosition = new SealPositionDO();
        if (!SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
            enterpriseSealPosition = sealPositionList.stream()
                    .filter(sealPositionDO -> AccountType.ENTERPRISE.getValue().equals(sealPositionDO.getAccountType()))
                    .filter(sealPositionDO -> ContractConstants.SEAL_POSITION_TYPE_REGULAR.equals(sealPositionDO.getType()))
                    .findFirst().orElseThrow(() -> new ParamException("未设置企业默认印章位置"));
        }
        if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
            personSealPosition = sealPositionList.stream()
                    .filter(sealPositionDO -> AccountType.PERSON.getValue().equals(sealPositionDO.getAccountType()))
                    .filter(sealPositionDO -> ContractConstants.SEAL_POSITION_TYPE_REGULAR.equals(sealPositionDO.getType()))
                    .findFirst().orElseThrow(() -> new ParamException("未设置个人默认印章位置"));
        }
        List<MemberVariableDTO> memberVariableList = new ArrayList<>();
        memberVariableList.add(publishSignSigngleDTO.getMemberVariableDTO());
        List<PartySignAddDTO> partySignList = new ArrayList<>();
        List<ContractVariableAddDTO> contractVariableList = new ArrayList<>();


        MultipartFile[] multipartFiles = new MultipartFile[memberVariableList.size()];
        List<Long> contractIdList = new ArrayList<>();

        // 合同签署发送微信通告
        List<SignWeiXinSendDTO> weiXinSendList = new ArrayList<>();

        stopWatch.stop();

        stopWatch.start("发起合同签署，数据库保存");
        String partyBAccountId = "";
        TemplateDO templateDO = templateService.selectDOById(publishSignSigngleDTO.getTemplateId(), tokenUser.getSiteId());
        ParamException.notNull(templateDO, "合同模板不存在,请检查");
        ParamException.hasText(templateDO.getContent(), "合同内容为空,请检查");
        // 替换变量
        memberVariableList.parallelStream().forEach(memberVariableDTO -> {
            if (!StringUtils.isBlank(memberVariableDTO.getTemplateContent())) {
                return;
            }
            // 替换合同中的变量
            String content = substitutionVariable(templateDO.getContent(), memberVariableDTO.getVariableList(), contractVariableList);
            memberVariableDTO.setTemplateContent(content);
        });

        //所有成员变量
        for (int i = 0; i < memberVariableList.size(); i++) {
            long replace = System.currentTimeMillis();
            MemberVariableDTO memberVariableDTO = memberVariableList.get(i);
            String replaceContent = memberVariableDTO.getTemplateContent();
            ParamException.hasText(replaceContent, "合同内容为空,请检查");
            String templateContent = HtmlUtils.htmlUnescape(replaceContent);
            Matcher contentMatcher = A_REG_PATTERN.matcher(templateContent);
            templateContent = contentMatcher.replaceAll("").replaceAll("</a>", "");
            log.info("publishTemplateSignSingle 发起合同签署，替换信息用时：{}毫秒", System.currentTimeMillis() - replace);
//            if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
            //查询成员账户
            AccountDO accountDO = accountService.findAccount(AccountType.find(memberVariableDTO.getAccountType()), memberVariableDTO.getMemberId());
            ParamException.notNull(accountDO, "员工未实名认证【" + memberVariableDTO.getPartyBName() + "】");
//            if (accountDO == null) {
//                //没有先新建一个未认证的账户
//                accountDO = new AccountDO();
//                accountDO.setMemberId(memberVariableDTO.getMemberId());
//                accountDO.setType(AccountType.PERSON.getValue());
//                accountDO.setReal(Boolean.FALSE);
//                accountDO = accountService.save(accountDO, DEFAULT_SITE_ID, tokenUser.getUserId());
//            }
            partyBAccountId = accountDO.getId();
//            }

            Long categoryId = publishSignSigngleDTO.getCategoryId();
            CategoryVO categoryVO = categoryService.selectById(categoryId, tokenUser.getSiteId());
            // 公司账户
            AccountVO accountVO;
            if (!SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
                SealVO sealVO = sealService.selectById(categoryVO.getEnterpriseSealId(), tokenUser.getSiteId());
                String accountId = sealVO.getAccountId();
                // 公司账户
                accountVO = accountService.selectById(accountId);
                //AccountDO account = accountService.findAccount(AccountType.ENTERPRISE, publishSignSigngleDTO.getSubjectId());
            } else {
                accountVO = new AccountVO();
            }

            // 每个合同需要初始化
            ContractDO contractDO = new ContractDO();
            contractDO.setCategoryId(categoryId);
            contractDO.setTemplateId(templateId);
            contractDO.setSubjectId(publishSignSigngleDTO.getSubjectId());
            contractDO.setPublishDate(memberVariableDTO.getPublishDate());
            contractDO.setSignDate(new Date());
            contractDO.setName(categoryDO.getName() + "-" + memberVariableDTO.getPartyBName());
            contractDO.setContractType(ContractType.ONLINE.getValue());
            contractDO.setFileType(categoryDO.getFileType());
            contractDO.setContractNo(memberVariableDTO.getContractNo());
            contractDO.setContent(templateContent);
            // 甲乙方名称
            contractDO.setPartyAAccountId(accountVO.getId());
            contractDO.setPartyAName(memberVariableDTO.getPartyAName());

            contractDO.setPartyBAccountId(partyBAccountId);
            contractDO.setPartyBName(memberVariableDTO.getPartyBName());
            contractDO.setPartyBPhone(memberVariableDTO.getPartyBPhone());
            // 合同开始时间结束时间
            contractDO.setEndDate(memberVariableDTO.getEndDate());
            contractDO.setDateline(memberVariableDTO.getDateline());
            contractDO.setFileType(memberVariableDTO.getContractType());
            // 是否需要人脸核身
            contractDO.setIsCheckFace(isCheckFace);
            // 小程序是否展示
            boolean appIsShow = true;
            // 公司id
            String companyId = contractDO.getSubjectId();
            // 获取公司的配置
            CompanyContractShowVO contractShow = companyContractShowService.findByCompanyId(companyId, tokenUser);
            if (Objects.nonNull(contractShow)) {
                appIsShow = contractShow.getIsShow();
            }
            contractDO.setAppIsShow(appIsShow);

            // 签署编号,当前时间+4位随机吗
            // 字符串形当前时间
            String dateStr = DateKit.getDateStr(DateKit.getToday(), "yyyyMMddHHmm");
            // 6位随机数
            String randomNum = RandomStringUtils.random(6, false, true);
            contractDO.setSignNo(dateStr + randomNum);

            if (SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                contractDO.setStatus(ContractConstants.CONTRACT_STATUS_ENTERPRISE);
            } else {
                contractDO.setStatus(ContractConstants.CONTRACT_STATUS_PERSON);
            }

            // 是否需要重新发起
            boolean needInitAgain = false;
            if (StringUtils.isNotEmpty(templateContent)) {
                // 有签署确认时间字段,需要重新发起
                needInitAgain = templateContent.contains(SIGN_CONFIRM_DATE_REPLACE);
            }
            contractDO.setNeedInitAgain(needInitAgain);
            //保存合同表
            contractDO = contractService.save(contractDO, siteId, tokenUser.getUserId());
            log.info("发起合同方法：publishTemplateSignSignle, 合同名称：{},合同Id:{}", contractDO.getName(), contractDO.getId());
            contractIdList.add(contractDO.getId());
            ContractVO contractVO = contractDO.to(ContractVO.class);
            contractVO.setMemberId(memberVariableDTO.getMemberId());
            contractVOList.add(contractVO);

            // 有员工签署的才发送
            if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                SignWeiXinSendDTO signWeiXinSend = new SignWeiXinSendDTO();
                signWeiXinSend.setContractId(contractDO.getId());
                signWeiXinSend.setContractName(memberVariableDTO.getContractName());
                signWeiXinSend.setContractTypeId(categoryId);
                signWeiXinSend.setExpireDays(categoryDO.getExpireDays());
                // 甲乙方名称
                signWeiXinSend.setAName(memberVariableDTO.getPartyAName());
                signWeiXinSend.setAUserId(memberVariableDTO.getMemberId());
                signWeiXinSend.setBName(memberVariableDTO.getPartyBName());

                weiXinSendList.add(signWeiXinSend);
            }
            long saveTime = System.currentTimeMillis();
            //保存合同pdf文件
            String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
            String filePath = fileProperties.getTempPath() + File.separator + fileName;
            FileHelper.saveFileByStream(new byte[]{}, filePath);
            log.info("publishTemplateSignSingle 发起合同签署，保存合同pdf文件用时：{}毫秒", System.currentTimeMillis() - saveTime);

            if (needInitAgain) {
                // 需要重新发起
                // 首次发起,替换合同签署确认时间为空时间串
                //      格式对应 '<signDateReplace>'
                //      格式对应 '                '
                templateContent = templateContent.replaceAll(SIGN_CONFIRM_DATE_REPLACE, "                ");
                // 再次发起,替换时间为当前时间
            }

            WkPdfHelper.exportPdf(filePath, templateContent);
            byte[] bytes = FileHelper.getFileBytes(filePath);

            String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
            MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), bytes);
            multipartFiles[i] = file;
            //保存企业签署方
            if (!SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
                ParamException.notNull(enterprisePartyAccountId, "企业方id不能为空");
                PartySignAddDTO enterprisePartySignAddDTO = new PartySignAddDTO();
                enterprisePartySignAddDTO.setAccountId(enterprisePartyAccountId);
                enterprisePartySignAddDTO.setContractId(contractDO.getId());
                enterprisePartySignAddDTO.setSealPositionId(enterpriseSealPosition.getId());
                enterprisePartySignAddDTO.setSealId(categoryDO.getEnterpriseSealId());
                enterprisePartySignAddDTO.setStatus(SIGN_STATUS_READY);
                partySignList.add(enterprisePartySignAddDTO);
            }
            //保存员工签署方
            if (!SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                PartySignAddDTO personalPartySignAddDTO = new PartySignAddDTO();
                personalPartySignAddDTO.setAccountId(partyBAccountId);
                personalPartySignAddDTO.setContractId(contractDO.getId());
                personalPartySignAddDTO.setSealPositionId(personSealPosition.getId());
                personalPartySignAddDTO.setStatus(SIGN_STATUS_READY);
                partySignList.add(personalPartySignAddDTO);
            }
            // 仅企业签署也需要保存员工签署方,为了兼容以前的逻辑,这里单独为企业单方签署做一下判断
            if (SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
                PartySignAddDTO personalPartySignAddDTO = new PartySignAddDTO();
                personalPartySignAddDTO.setAccountId(partyBAccountId);
                personalPartySignAddDTO.setContractId(contractDO.getId());
                personalPartySignAddDTO.setSealPositionId(personSealPosition.getId());
                // 状态为签署完成
                personalPartySignAddDTO.setStatus(SIGN_STATUS_FINISH);
                partySignList.add(personalPartySignAddDTO);
            }
            for (ContractVariableAddDTO variableAddDTO : contractVariableList) {
                // 放入保存后合同的Id
                variableAddDTO.setContractId(contractDO.getId());
            }
        }
        // 合同Id去重
        List<Long> distinctIds = contractIdList.parallelStream().distinct().collect(Collectors.toList());
        log.info("publishTemplateSignSingle 发起合同签署，数据库开始保存：{}毫秒", System.currentTimeMillis() - startTimeMillis);
        partySignService.batchSave(partySignList, tokenUser);
        contractVariableService.batchSave(contractVariableList, tokenUser);
        stopWatch.stop();

        stopWatch.start("发起合同签署，上传合同pdf文件");
        // 换用异步上传OSS
//        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//        singleThreadExecutor.execute(() -> {
        //上传合同文件到oss
        List<String> pathList;
        long ossTime = System.currentTimeMillis();
        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (showDiffSite) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(multipartFiles, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(multipartFiles, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }
        stopWatch.stop();

        stopWatch.start("发起合同签署，执行结束");
        //保存所有合同文件路径
        for (int i = 0; i < distinctIds.size(); i++) {
            //把签署前的文件删除
            AppendixDO originFile = appendixService.selectByContractId(distinctIds.get(i), tokenUser.getSiteId());
            if (originFile != null) {
                appendixService.deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), originFile.getId());
            }
            FileVersionDO fileVersionDO = siteFileService.download(Collections.singletonList(pathList.get(i)), null,
                    tokenUserSite);
            ParamException.notNull(fileVersionDO, "文件不存在或已删除");
            File pdf = new File(fileVersionDO.getTempFilePath());
            // 生成jpg
            String fileName = System.currentTimeMillis() + ".jpg";
            String filePath = fileProperties.getTempPath() + File.separator + fileName;
            File file = new File(filePath);
            PdfUtil.pdf2multiImage(pdf.getAbsolutePath(), filePath, 80);
            log.info("=合同文件pdf转jpg,文件:{},文件地址:{}", file, filePath);
            MultipartFile[] multipartFilesJpg = new MultipartFile[1];
            try {
                FileInputStream fileInputStreamTwo = new FileInputStream(file);
                MultipartFile multipartFile = new MockMultipartFile("copy" + file.getName(), file.getName(),
                        ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStreamTwo);
                multipartFilesJpg[0] = multipartFile;
            } catch (IOException e) {
                log.error("文件转化失败", e);
                throw new ParamException("文件转化失败!");
            }
            List<String> downloadReport = siteFileService.upload(multipartFilesJpg, "attachment", tokenUser);
            log.info("=合同文件jpg上传,地址:{}", String.join(",", downloadReport));
            ContractException.isFalse(CollectionUtils.isEmpty(downloadReport), "上传文件出错了,请稍后重试或联系管理员");
            // 删除生成的图片
            //file.delete();
            AppendixDO appendixDO = new AppendixDO();
            appendixDO.setPath(pathList.get(i));
            appendixDO.setName(downloadReport.get(0));
            appendixDO.setContractId(distinctIds.get(i));
            appendixDO.setCurrentPage(0);
            log.info("=合同文件保存,保存前DO:{}", appendixDO);
            AppendixDO save = appendixService.save(appendixDO, siteId, tokenUser.getUserId());
            log.info("=合同文件保存,保存后DO:{},ID:{}", save, save.getId());
        }

        //合同发起回调函数
        this.contractPublishCallback(distinctIds, tokenUser);

        if (!CollectionUtils.isEmpty(weiXinSendList)) {
            // 发送微信公众号模板推送消息
            this.sendMessageCallback(weiXinSendList, publishSignSigngleDTO.getSubjectName(), tokenUser);
        }
        stopWatch.stop();

        stopWatch.start("发起合同签署，保存日志");
//                contractVO.setPdfFilePath(String.join(",", pathList));
//        });
        try {
            // 保存log日志
            ILogProviderService logProviderService = BeanKit.getBean(ILogProviderService.class);
            if (logProviderService == null) {
                log.error("logProviderService is null");
                return contractVOList;
            }
            String content;
            if (CollectionUtils.isEmpty(memberVariableList)) {
                return contractVOList;
            }
            String partyBName = memberVariableList.get(0).getPartyBName();
            if (memberVariableList.size() == 1) {
                content = partyBName + "由管理员 " + tokenUser.getUserName() + " 发起合同签署";
            } else {
                content = partyBName + "等" + memberVariableList.size() + "人由管理员" + tokenUser.getUserName() + "发起合同签署";
            }
            logProviderService.log(content, ActionType.SAVE.getValue(), "Sign"
                    , publishSignSigngleDTO.getSubjectId(), publishSignSigngleDTO.getSubjectId(), tokenUser);
        } catch (Exception e) {
            log.error("contract保存日志出错了", e);
        }
        stopWatch.stop();
        log.info("生成合同耗时：{}", stopWatch.prettyPrint());
        return contractVOList;
    }


    // 替换变量
    @Override
    public String substitutionVariable(String content, List<Map<String, Object>> variableList, List<ContractVariableAddDTO> contractVariableList) {

        // 前端没传 自己手动替换
        for (Map<String, Object> stringObjectMap : variableList) {
            for (String key : stringObjectMap.keySet()) {
                Object o = stringObjectMap.get(key);
                JSONObject jsonObject = (JSONObject) JSON.toJSON(o);
                if (jsonObject == null) {
                    continue;
                }
                String name = jsonObject.getString("name");
                Integer id = jsonObject.getInteger("id");
                StringBuilder value = Optional.ofNullable(jsonObject.getString("content"))
                        .map(StringBuilder::new).orElse(null);
                if (!key.equals(name)) {
                    continue;
                }
                // 变量名加两侧#
                String thisName = "#" + name + "#";
                // 字数不够，空格来凑
                StringBuilder kongge = new StringBuilder();
                if (StringUtils.isBlank(value == null ? null : value.toString())) {
                    // 值为空时替换为同等数量的空格
                    value = new StringBuilder();
                    for (int j = 0; j < name.length() + 2; j++) {
                        value.append("&nbsp;");
                    }
                } else {
                    if (thisName.length() > value.length()) {
                        // 多出来的字用空格代替
                        int num = thisName.length() - value.length();
                        for (int i = 0; i < num; i++) {
                            kongge.append("&nbsp;");
                        }
                    }
                }
                content = content.replace(thisName, kongge.toString() + value + kongge);
                if (id != null) {
                    //合同关联的模板变量
                    ContractVariableAddDTO contractVariableAddDTO = new ContractVariableAddDTO();
                    contractVariableAddDTO.setVariableId(new Long(id));
                    contractVariableAddDTO.setContent(value.toString());
                    contractVariableList.add(contractVariableAddDTO);
                }
            }
        }
        return content;
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void contractSignAgain(Long contractId, String companyId, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在");

        //获取账户对象
        AccountDO accountCompanyDO = accountService.findAccount(AccountType.ENTERPRISE, companyId);
        ParamException.notNull(accountCompanyDO, "企业未认证");
        ParamException.isTrue(accountCompanyDO.getReal(), "企业未认证");
        //获取签署方
        PartySignDO partySignCompanyDO = partySignService.selectByContractParty(contractId, accountCompanyDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignCompanyDO, "未找到企业合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignCompanyDO.getStatus()), "合同状态不正确");

        // 个人签名
        PersonSignatureDO personSignatureDO = personSignatureService.findByContractId(contractId, tokenUser.getSiteId());

        // 合同需要重新发起
        if (contractDO.getNeedInitAgain() && Objects.nonNull(personSignatureDO)) {
            //保存合同pdf文件
            String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
            String filePath = fileProperties.getTempPath() + File.separator + fileName;
            FileHelper.saveFileByStream(new byte[]{}, filePath);

            String templateContent = contractDO.getContent();

            // 当时个人签署时的人
            TokenUser tokenUserPerSon = new TokenUser();
            tokenUserPerSon.setSiteId(tokenUser.getSiteId());
            tokenUserPerSon.setUserId(personSignatureDO.getUserId());

            // 需要重新发起
            // 再次发起,替换时间为当前时间
            //      格式对应 'signDateReplace'
            //      格式对应 '  year年 month月 day日 '
            String year = DateFormatUtils.format(new Date(), "yyyy");
            String month = DateFormatUtils.format(new Date(), "MM");
            String day = DateFormatUtils.format(new Date(), "dd");
            String ymd = DateFormatUtils.format(new Date(), "yyyy-MM-dd");

            String dateStr = "  " + year + "年 " + month + "月 " + day + "日 ";

            templateContent = templateContent.replaceAll(SIGN_CONFIRM_DATE_REPLACE, ymd);
            WkPdfHelper.exportPdf(filePath, templateContent);
            byte[] bytes = FileHelper.getFileBytes(filePath);

            //获取签署人账户对象
            AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUserPerSon.getUserId());
            ParamException.notNull(accountDO, "用户未实名");
            ParamException.isTrue(accountDO.getReal(), "用户未实名");
            //获取签署方对象
            PartySignDO partySignDO = partySignService.selectByContractParty(contractId, accountDO.getId(), tokenUserPerSon.getSiteId());
            ParamException.notNull(partySignDO, "未找到合同签署方");

            // 根据合同签署文件字节流个人签署
            //获取签署位置
            SealPositionQuery sealPositionQuery = new SealPositionQuery();
            if (contractDO.getTemplateId() != null) {
                sealPositionQuery.setTemplateId(contractDO.getTemplateId());
            }
            sealPositionQuery.setAccountType(AccountType.PERSON.getValue());
            sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
            List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
            //调用第三方进行签署
            FileDigestSignResult fileDigestSignResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                    , personSignatureDO.getSeal(), bytes, sealPositionList, SealType.SINGLE, tokenUser);
            byte[] resultStream = fileDigestSignResult.getStream();
            log.info("合同日志追踪===>公司作弊签署个人合同重签，调用第三方签署成功，userId:{},accountId:{},name:{},合同md5:{}",
                    tokenUser.getUserId(), accountDO.getAccountId(), accountDO.getName(),
                    DigestUtils.md5DigestAsHex(resultStream));
            // 更新个人的合同签署方签署记录中
            // 获取个人签署方
            PartySignDO partySignPersonalDO = partySignService.selectByContractParty(contractId
                    , contractDO.getPartyBAccountId(), tokenUser.getSiteId());
            ParamException.notNull(partySignCompanyDO, "未找到个人合同签署方");
            partySignCompanyDO.setSignServiceId(fileDigestSignResult.getSignServiceId());
            partySignCompanyDO.setSignDetailUrl(fileDigestSignResult.getSignDetailUrl());
            partySignService.updateByIdSelective(partySignPersonalDO, tokenUser.getSiteId(), tokenUser.getUserId());
            // 根据字节流进行签署
            enterpriseSignByBytes(contractDO, accountCompanyDO, partySignCompanyDO, resultStream, Boolean.FALSE, tokenUser);
        } else {
            // 企业签署
            enterpriseSign(companyId, contractId, tokenUser);
        }
    }

    @Override
    public void contractSignAgain2(Long contractId, String companyId, byte[] bytes, boolean showDiffSite, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在");
        // 企业签署
        ParamException.notNull(contractId, "合同id不能为空");
        //获取账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.ENTERPRISE, companyId);
        ParamException.notNull(accountDO, "企业未认证");
        ParamException.isTrue(accountDO.getReal(), "企业未认证");
        //获取签署方
        PartySignDO partySignDO = partySignService.selectByContractParty(contractId, accountDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignDO, "未找到合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignDO.getStatus()), "合同状态不正确");
        // 根据字节流进行签署
        enterpriseSignByBytes2(contractDO, accountDO, partySignDO, bytes, showDiffSite, tokenUser);

    }

    @Async
    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void contractSignAgainAsync(Long contractId, String companyId, TokenUser tokenUser) {
        log.info("公司确认签署合同开始 contractId: " + contractId + " " + Thread.currentThread().getName());
        contractSignAgain(contractId, companyId, tokenUser);
        log.info("公司确认签署合同结束 contractId: " + contractId + " " + Thread.currentThread().getName());
    }

    /**
     * 企业单方签署发送微信消息通知
     *
     * @param weiXinSend 要发送的人
     * @param tokenUser  当前用户
     */
    @Async
    @Override
    public void sendWaitViewWeiXinTemp(SignWeiXinSendDTO weiXinSend, TokenUser tokenUser) {
        // 用缓存来控制发不发通知
        String sendNoticeLockKey = CacheKeyKit.getSendNoticeLockKey();
        boolean isSend = cache.get(sendNoticeLockKey);
        if (!ObjectUtils.isEmpty(isSend) && !isSend) {
            log.info("缓存控制不发微信或短信通知");
            return;
        }
        if (ObjectUtils.isEmpty(weiXinSend)) {
            return;
        }
        String appId = siteConfigProviderService.selectValueByCode(CODE_APPID, tokenUser.getSiteId());
        String waitSignTempId = siteConfigProviderService.selectValueByCode(CODE_WAIT_SIGN_TEMP_ID, tokenUser.getSiteId());
        String miNiAppId = siteConfigProviderService.selectValueByCode(CODE_MINI_APPID, tokenUser.getSiteId());
        String miNiPagePath = siteConfigProviderService.selectValueByCode(CODE_PATH_CONTRACTSIGN, tokenUser.getSiteId());

        if (StringUtils.isEmpty(appId) || StringUtils.isEmpty(waitSignTempId)) {
            log.error("未配置微信模板id，不发送微信模板消息");
            return;
        }
        String userId = weiXinSend.getAUserId();
        UnionOpenIdDO unionOpenId = userProviderService.getUnionOpenIdByUser(userId, appId);
        if (Objects.nonNull(unionOpenId) && StringUtils.isNotEmpty(unionOpenId.getOpenId())) {
            String openId = unionOpenId.getOpenId();
            // 推送微信模板消息
            WeiXinDTO weiXinDTO = new WeiXinDTO();
            weiXinDTO.setAppId(appId);
            weiXinDTO.setTemplateId(waitSignTempId);
            weiXinDTO.setToUser(openId);
            // 参数
            List<WxTemplateParam> paramList = new ArrayList<>();
            WxTemplateParam param1 = new WxTemplateParam();
            param1.setName("first");
            param1.setValue("您好！您有一份电子合同待查看");
            paramList.add(param1);

            WxTemplateParam param2 = new WxTemplateParam();
            param2.setName("keyword1");
            param2.setValue(weiXinSend.getContractName());
            paramList.add(param2);

            WxTemplateParam param3 = new WxTemplateParam();
            param3.setName("keyword2");
            param3.setValue(weiXinSend.getAName());
            paramList.add(param3);

            WxTemplateParam param4 = new WxTemplateParam();
            param4.setName("keyword3");
            param4.setValue(weiXinSend.getBName());
            paramList.add(param4);

            WxTemplateParam param5 = new WxTemplateParam();
            param5.setName("keyword4");
            param5.setValue("");
            paramList.add(param5);

            WxTemplateParam param6 = new WxTemplateParam();
            param6.setName("keyword5");
            param6.setValue("");
            paramList.add(param6);

            weiXinDTO.setTemplateParamList(paramList);
            // 跳转小程序
            WxMiNiParam wxMiNiParam = new WxMiNiParam();
            wxMiNiParam.setAppid(miNiAppId);
            wxMiNiParam.setPagepath(miNiPagePath + "?status=3&contractId=" + weiXinSend.getContractId());
            weiXinDTO.setMiniProgram(wxMiNiParam);
            messageProviderService.sendWeChatTempMessage(weiXinDTO, tokenUser);
        } else {
            // 发不了微信通知就发短信
            log.info("发不了微信通知就发短信,userId:{}, appId：{}", userId, appId);
            sendWaitViewSms(userId, tokenUser);
        }
    }

    /**
     * 异步发送有合同待签署-微信公众号模板消息
     *
     * @param weiXinSendList 要发送的人
     * @param subjectName    用工单位名称
     * @param tokenUser      当前用户
     */
    @Async
    void sendWaitSignWeiXinTemp(List<SignWeiXinSendDTO> weiXinSendList, String subjectName, TokenUser tokenUser) {
        // 测试环境直接返回 不发送通知
        if (contractProperties.getIsAPNs() != null && contractProperties.getIsAPNs() == 0) {
            log.info("测试环境不发微信通知");
            return;
        }

        if (CollectionUtils.isEmpty(weiXinSendList)) {
            return;
        }

        String appId = siteConfigProviderService.selectValueByCode(CODE_APPID, tokenUser.getSiteId());
        String waitSignTempId = siteConfigProviderService.selectValueByCode(CODE_WAIT_SIGN_TEMP_ID, tokenUser.getSiteId());
        String miNiAppId = siteConfigProviderService.selectValueByCode(CODE_MINI_APPID, tokenUser.getSiteId());
        String miNiPagePath = siteConfigProviderService.selectValueByCode(CODE_PATH_CONTRACTSIGN, tokenUser.getSiteId());

        if (StringUtils.isEmpty(appId) || StringUtils.isEmpty(waitSignTempId)) {
            log.error("未配置微信模板id，不发送微信模板消息");
            return;
        }
        // 合同有效天数
        Integer expireDays = weiXinSendList.get(0).getExpireDays();

        for (SignWeiXinSendDTO weiXinSend : weiXinSendList) {
            String userId = weiXinSend.getAUserId();
            UnionOpenIdDO unionOpenId = userProviderService.getUnionOpenIdByUser(userId, appId);
            if (Objects.nonNull(unionOpenId) && StringUtils.isNotEmpty(unionOpenId.getOpenId())) {
                String openId = unionOpenId.getOpenId();
                // 推送微信模板消息
                WeiXinDTO weiXinDTO = new WeiXinDTO();
                weiXinDTO.setAppId(appId);
                weiXinDTO.setTemplateId(waitSignTempId);
                weiXinDTO.setToUser(openId);
                // 参数
                List<WxTemplateParam> paramList = new ArrayList<>();
                WxTemplateParam param1 = new WxTemplateParam();
                param1.setName("first");
                if (StrKit.isEmpty(subjectName)) {
                    param1.setValue("您好,您有一份电子合同需要签署");
                } else {
                    param1.setValue("您好,您有一份电子合同需要签署「" + subjectName + "」");
                }
                paramList.add(param1);

                WxTemplateParam param2 = new WxTemplateParam();
                param2.setName("keyword1");
                param2.setValue(weiXinSend.getContractName());
                paramList.add(param2);

                WxTemplateParam param3 = new WxTemplateParam();
                param3.setName("keyword2");
                param3.setValue(weiXinSend.getAName());
                paramList.add(param3);

                WxTemplateParam param4 = new WxTemplateParam();
                param4.setName("keyword3");
                param4.setValue(weiXinSend.getBName());
                paramList.add(param4);

                WxTemplateParam param5 = new WxTemplateParam();
                param5.setName("keyword4");
                // 无期限的先默认100天
                if (expireDays == null || expireDays == -1) {
                    param5.setValue("长期有效");
                } else {
                    Date expireDate = DateKit.getBeforeDay(new Date(), expireDays);
                    param5.setValue(DateKit.getDateTimeStr(expireDate));
                }
                paramList.add(param5);

                WxTemplateParam param6 = new WxTemplateParam();
                param6.setName("keyword5");
                param6.setValue("请在截止日期前完成签署");
                paramList.add(param6);

                weiXinDTO.setTemplateParamList(paramList);
                // 跳转小程序
                WxMiNiParam wxMiNiParam = new WxMiNiParam();
                wxMiNiParam.setAppid(miNiAppId);
                wxMiNiParam.setPagepath(miNiPagePath + "?status=0&contractId=" + weiXinSend.getContractId());
                weiXinDTO.setMiniProgram(wxMiNiParam);
                messageProviderService.sendWeChatTempMessage(weiXinDTO, tokenUser);
            } else {
                // 发不了微信通知就发短信
                log.info("发不了微信通知就发短信,userId:{}, appId：{}", userId, appId);
                sendSms(userId, tokenUser);
            }
        }
    }

    /**
     * 发送短信
     */
    private void sendSms(String userId, TokenUser tokenUser) {
        UserVO userVO = userProviderService.selectById(userId);

        ShortMessageDTO shortMessageDTO = new ShortMessageDTO();
        shortMessageDTO.setScene(SCENE_SIGN_NOTICE_CERTIFICATION);
        // 写入短信内容
        shortMessageDTO.setPhone(userVO.getPhone());
        try {
            messageProviderService.sendShortMessage(shortMessageDTO, tokenUser);
        } catch (Exception e) {
            log.error("合同签署发送短信失败,失败信息{}", e.getMessage());
        }
    }

    /**
     * 发送短信
     */
    private void sendWaitViewSms(String userId, TokenUser tokenUser) {
        UserVO userVO = userProviderService.selectById(userId);

        ShortMessageDTO shortMessageDTO = new ShortMessageDTO();
        shortMessageDTO.setScene(ENTERPRISE_CONTRACT_NOTICE);
        // 写入短信内容
        shortMessageDTO.setPhone(userVO.getPhone());
        try {
            messageProviderService.sendShortMessage(shortMessageDTO, null);
        } catch (Exception e) {
            log.error("合同签署发送短信失败,失败信息{}", e.getMessage());
        }
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void personSignNoPhone(Long contractId, String sealString, TokenUser tokenUser) {
        // 个人签署
        personSignByInfo(contractId, sealString, Boolean.FALSE, tokenUser);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void personSign(Long contractId, String sealString, String code, boolean isBlank, TokenUser tokenUser) {
        personSignBySite(contractId, sealString, code, isBlank, Boolean.FALSE, tokenUser);
    }

    @Override
    public void personSign2(Long contractId, byte[] stream, Boolean showDiffSite, TokenUser tokenUser) {
        String sealString = null;
        //获取签署人账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUser.getUserId());
        ParamException.notNull(accountDO, "用户未实名");
        ParamException.isTrue(accountDO.getReal(), "用户未实名");
        // 个人签署
        personSignByInfo2(contractId, stream, showDiffSite, tokenUser);
        // 将个人签名保存起来
        personSignatureService.pubshPersonSignature(contractId, sealString, tokenUser);
    }

    /**
     * 个人合同签署
     *
     * @param contractId 合同id
     * @param tokenUser  当前用户
     */
    private void personSignByInfo2(Long contractId, byte[] stream, Boolean showDiffSite, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在");
        //获取签署人账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUser.getUserId());
        ParamException.notNull(accountDO, "用户未实名");
        ParamException.isTrue(accountDO.getReal(), "用户未实名");
        //获取签署方对象
        PartySignDO partySignDO = partySignService.selectByContractParty(contractId, accountDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignDO, "未找到合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignDO.getStatus()), "合同已过期");

        // 根据合同签署文件字节流签署
        personSignByBytes2(stream, contractDO, accountDO, partySignDO, showDiffSite, tokenUser);
    }

    /**
     * 根据合同签署文件字节流签署
     *
     * @param stream       合同内容字节流
     * @param contractDO   合同
     * @param accountDO    账号
     * @param partySignDO  签署方
     * @param showDiffSite 合同文件不同站点显示
     * @param tokenUser    当前用户
     */
    private void personSignByBytes2(byte[] stream, ContractDO contractDO, AccountDO accountDO, PartySignDO partySignDO,
                                    Boolean showDiffSite, TokenUser tokenUser) {
        Long contractId = contractDO.getId();
        //获取签署位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        if (contractDO.getTemplateId() != null) {
            sealPositionQuery.setTemplateId(contractDO.getTemplateId());
        }
        sealPositionQuery.setAccountType(AccountType.PERSON.getValue());
        sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
        String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
        String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
        MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), stream);
        MultipartFile[] files = new MultipartFile[1];
        files[0] = file;
        //上传签署后的合同文件
        List<String> pathList;

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (showDiffSite) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }

        //把签署前的文件删除
        AppendixDO originFile = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        if (originFile != null) {
            appendixService.deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), originFile.getId());
        }
        AppendixDO appendixDO = new AppendixDO();
        if (!CollectionUtils.isEmpty(pathList)) {
            String path = pathList.get(0);
            appendixDO.setContractId(contractId);
            appendixDO.setCurrentPage(0);
            appendixDO.setPath(path);
            //保存签署后的合同文件
            appendixService.save(appendixDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        //修改签署方状态
        partySignDO.setSignDate(new Date());
        partySignDO.setStatus(SIGN_STATUS_FINISH);
        partySignService.updateByIdSelective(partySignDO, tokenUser.getSiteId(), tokenUser.getUserId());
        //修改合同状态
        CategoryDO categoryDO = categoryService.selectDOById(contractDO.getCategoryId(), tokenUser.getSiteId());

        if (!SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
            //待企业签署
            contractDO.setStatus(CONTRACT_STATUS_ENTERPRISE);
        } else {
            //签署完毕
            contractDO.setStatus(CONTRACT_STATUS_FINISH);
            // 签署完成时间
            contractDO.setSignFinishDate(new Date());
        }
        //签署成功回调
        this.contractSignSuccessCallback(contractDO, appendixDO, tokenUser);
        // 员工签署时间使用当前日期
        contractDO.setEmpSignDate(new Date());
        contractService.updateByIdSelective(contractDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    private void personSignBySite(Long contractId, String sealString, String code, boolean isBlank, Boolean showDiffSite, TokenUser tokenUser) {
        //获取签署人账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUser.getUserId());
        ParamException.notNull(accountDO, "用户未实名");
        ParamException.isTrue(accountDO.getReal(), "用户未实名");
        if (!isBlank) {
            //调用消息接口验证验证码
            Boolean result = this.checkCodeProvider(accountDO.getMobile(), code, tokenUser);
            //验证成功
            if (!result) {
                throw new ParamException("验证码不正确");
            }
        }
        // 个人签署
        personSignByInfo(contractId, sealString, showDiffSite, tokenUser);

        // 将个人签名保存起来
        personSignatureService.pubshPersonSignature(contractId, sealString, tokenUser);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void personSignDiffSite(Long contractId, String sealString, String code, TokenUser tokenUser) {
        personSignBySite(contractId, sealString, code, Boolean.FALSE, Boolean.TRUE, tokenUser);
    }

    /**
     * 个人合同签署
     *
     * @param contractId   合同id
     * @param sealString   个人签名
     * @param showDiffSite 合同文件不同站点显示
     * @param tokenUser    当前用户
     */
    private void personSignByInfo(Long contractId, String sealString, Boolean showDiffSite, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在");
        //获取签署人账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUser.getUserId());
        ParamException.notNull(accountDO, "用户未实名");
        ParamException.isTrue(accountDO.getReal(), "用户未实名");
        //获取签署方对象
        PartySignDO partySignDO = partySignService.selectByContractParty(contractId, accountDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignDO, "未找到合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignDO.getStatus()), "合同已过期");

        //获取签署文件字节流
        byte[] bytes = this.getContractFileStream(contractDO, showDiffSite, tokenUser);

        // 根据合同签署文件字节流签署
        personSignByBytes(sealString, contractDO, accountDO, partySignDO, bytes, showDiffSite, tokenUser);
    }

    /**
     * 根据合同签署文件字节流签署
     *
     * @param sealString   个人签名
     * @param contractDO   合同
     * @param accountDO    账号
     * @param partySignDO  签署方
     * @param bytes        合同文件字节流
     * @param showDiffSite 合同文件不同站点显示
     * @param tokenUser    当前用户
     */
    private void personSignByBytes(String sealString, ContractDO contractDO, AccountDO accountDO, PartySignDO partySignDO, byte[] bytes, Boolean showDiffSite, TokenUser tokenUser) {
        Long contractId = contractDO.getId();
        //获取签署位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        if (contractDO.getTemplateId() != null) {
            sealPositionQuery.setTemplateId(contractDO.getTemplateId());
        }
        sealPositionQuery.setAccountType(AccountType.PERSON.getValue());
        sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
        List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        //调用第三方进行签署
        FileDigestSignResult fileDigestSignResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                , sealString, bytes, sealPositionList, SealType.SINGLE, tokenUser);
        byte[] resultStream = fileDigestSignResult.getStream();
        String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
        log.info("合同日志追踪===>个人调用第三方签署成功，userId:{},accountId:{},name:{},合同md5:{}",
                tokenUser.getUserId(), accountDO.getAccountId(), accountDO.getName(),
                DigestUtils.md5DigestAsHex(resultStream));
        String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
        MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), resultStream);
        MultipartFile[] files = new MultipartFile[1];
        files[0] = file;
        //上传签署后的合同文件
        List<String> pathList;

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (showDiffSite) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }

        //把签署前的文件删除
        AppendixDO originFile = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        if (originFile != null) {
            appendixService.deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), originFile.getId());
        }
        AppendixDO appendixDO = new AppendixDO();
        if (!CollectionUtils.isEmpty(pathList)) {
            String path = pathList.get(0);
            appendixDO.setContractId(contractId);
            appendixDO.setCurrentPage(0);
            appendixDO.setPath(path);
            //保存签署后的合同文件
            appendixService.save(appendixDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        //修改签署方状态
        partySignDO.setSignDate(new Date());
        partySignDO.setStatus(SIGN_STATUS_FINISH);
        partySignDO.setSignServiceId(fileDigestSignResult.getSignServiceId());
        partySignDO.setSignDetailUrl(fileDigestSignResult.getSignDetailUrl());
        partySignService.updateByIdSelective(partySignDO, tokenUser.getSiteId(), tokenUser.getUserId());
        //修改合同状态
        CategoryDO categoryDO = categoryService.selectDOById(contractDO.getCategoryId(), tokenUser.getSiteId());

        if (!SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
            //待企业签署
            contractDO.setStatus(CONTRACT_STATUS_ENTERPRISE);
        } else {
            //签署完毕
            contractDO.setStatus(CONTRACT_STATUS_FINISH);
            // 签署完成时间
            contractDO.setSignFinishDate(new Date());
        }
        //签署成功回调
        this.contractSignSuccessCallback(contractDO, appendixDO, tokenUser);
        // 员工签署时间使用当前日期
        contractDO.setEmpSignDate(new Date());
        contractService.updateByIdSelective(contractDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void enterpriseSign(String companyId, Long contractId, TokenUser tokenUser) {
        enterpriseSignBySite(companyId, contractId, Boolean.FALSE, tokenUser);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void enterpriseSignDiffSite(String companyId, Long contractId, TokenUser tokenUser) {
        enterpriseSignBySite(companyId, contractId, Boolean.TRUE, tokenUser);
    }

    /**
     * 企业签署合同
     *
     * @param companyId    公司id
     * @param contractId   合同id
     * @param showDiffSite 合同在不同站点显示
     * @param tokenUser    当前用户
     */
    private void enterpriseSignBySite(String companyId, Long contractId, Boolean showDiffSite, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        //获取账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.ENTERPRISE, companyId);
        ParamException.notNull(accountDO, "企业未认证");
        ParamException.isTrue(accountDO.getReal(), "企业未认证");
        //获取签署方
        PartySignDO partySignDO = partySignService.selectByContractParty(contractId, accountDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignDO, "未找到合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignDO.getStatus()), "合同状态不正确");
        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在");

        //获取待签署文件字节流
        byte[] bytes = this.getContractFileStream(contractDO, showDiffSite, tokenUser);

        // 根据字节流进行签署
        enterpriseSignByBytes(contractDO, accountDO, partySignDO, bytes, showDiffSite, tokenUser);
    }

    /**
     * 企业签署合同
     *
     * @param companyId    公司id
     * @param contractId   合同id
     * @param showDiffSite 合同在不同站点显示
     * @param tokenUser    当前用户
     */
    private void enterpriseSignBySite2(String companyId, Long contractId, Boolean showDiffSite, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        //获取账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.ENTERPRISE, companyId);
        ParamException.notNull(accountDO, "企业未认证");
        ParamException.isTrue(accountDO.getReal(), "企业未认证");
        //获取签署方
        PartySignDO partySignDO = partySignService.selectByContractParty(contractId, accountDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignDO, "未找到合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignDO.getStatus()), "合同状态不正确");
        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在");

        //获取待签署文件字节流
        byte[] bytes = this.getContractFileStream(contractDO, showDiffSite, tokenUser);

        // 根据字节流进行签署
        enterpriseSignByBytes(contractDO, accountDO, partySignDO, bytes, showDiffSite, tokenUser);
    }

    /**
     * 公司根据字节流签署
     *
     * @param contractDO   合同
     * @param accountDO    账号
     * @param partySignDO  签署方
     * @param bytes        字节流
     * @param showDiffSite 合同在不同站点中展示
     * @param tokenUser    当前用户
     */
    private void enterpriseSignByBytes(ContractDO contractDO, AccountDO accountDO, PartySignDO partySignDO
            , byte[] bytes, Boolean showDiffSite, TokenUser tokenUser) {
        Long contractId = contractDO.getId();
        //从合同分类获取企业印章id
        CategoryDO categoryDO = categoryService.selectDOById(contractDO.getCategoryId(), tokenUser.getSiteId());
        Long sealId = categoryDO.getEnterpriseSealId();
        SealDO sealDO = sealService.selectDOById(sealId, tokenUser.getSiteId());
        ParamException.notNull(sealDO, "合同印章不存在，请设置或更换印章后重试");
        String sealString = sealDO.getSealString();

        //获取签署位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        if (contractDO.getTemplateId() != null) {
            sealPositionQuery.setTemplateId(contractDO.getTemplateId());
        }
        sealPositionQuery.setAccountType(AccountType.ENTERPRISE.getValue());
        sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
        List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        //调用第三方进行签署
        FileDigestSignResult fileDigestSignResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                , sealString, bytes, sealPositionList, SealType.SINGLE, tokenUser);
        byte[] resultStream = fileDigestSignResult.getStream();
        // 如果印章拥有法人章  并且模板设置了企业法人章的位置
        if (sealDO.getIsLegalPerson() != null && sealDO.getIsLegalPerson()
                && StrKit.isNotEmpty(sealDO.getLegalPersonPicture())) {
            // 查询法人章的位置
            sealPositionQuery = new SealPositionQuery();
            if (contractDO.getTemplateId() != null) {
                sealPositionQuery.setTemplateId(contractDO.getTemplateId());
            }
            sealPositionQuery.setAccountType(AccountType.LEGAL_PERSON.getValue());
            sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
            sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
            if (CollectionKit.isNotEmpty(sealPositionList)) {
                //调用第三方进行签署
                fileDigestSignResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                        , sealDO.getLegalPersonPicture(), resultStream, sealPositionList, SealType.SINGLE, 90F, tokenUser);
                resultStream = fileDigestSignResult.getStream();
            }
        }
        String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
        log.info("合同日志追踪===>公司签署，调用第三方签署成功，userId:{},accountId:{},name:{},合同md5:{}",
                tokenUser.getUserId(), accountDO.getAccountId(), accountDO.getName(),
                DigestUtils.md5DigestAsHex(resultStream));
        String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
        MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), resultStream);
        MultipartFile[] files = new MultipartFile[1];
        files[0] = file;
        //上传签署后的合同文件
        List<String> pathList;

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (showDiffSite) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }

        //把签署前的文件删除
        AppendixDO originFile = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        log.info("=合同文件,签署前文件DO:{},ID:{}", originFile, originFile.getId());
        if (originFile != null) {
            appendixService.deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), originFile.getId());
        }
        AppendixDO appendixDO = new AppendixDO();
        if (!CollectionUtils.isEmpty(pathList)) {
            appendixDO.setContractId(contractId);
            appendixDO.setCurrentPage(0);
            appendixDO.setPath(pathList.get(0));
            //保存签署后的合同文件
            AppendixDO save = appendixService.save(appendixDO, tokenUser.getSiteId(), tokenUser.getUserId());
            log.info("=合同文件,重新创建后文件DO:{},ID:{}", save, save.getId());
        }
        //更新签署方签署状态
        partySignDO.setSignDate(new Date());
        partySignDO.setStatus(SIGN_STATUS_FINISH);
        partySignDO.setSignServiceId(fileDigestSignResult.getSignServiceId());
        partySignDO.setSignDetailUrl(fileDigestSignResult.getSignDetailUrl());
        partySignService.updateByIdSelective(partySignDO, tokenUser.getSiteId(), tokenUser.getUserId());
        //修改合同状态
        contractDO.setStatus(CONTRACT_STATUS_FINISH);
        // 签署完成时间
        contractDO.setSignFinishDate(new Date());
        // 更新合同状态
        contractService.updateByIdSelective(contractDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 双方签署的合同,合同签署完成后通知用户
        if (SignMethod.BOTH.getValue().equals(categoryDO.getSignType())) {
            List<PartySignDO> partySignList = partySignService.selectByContractId(contractId, tokenUser.getSiteId());
            // 获取除公司签署方外的签署完成的签署方
            List<String> accountList = partySignList.stream().filter(partySign -> {
                // 是公司的签署方
                if (partySign.getId().equals(partySignDO.getId())) {
                    return false;
                }
                // 不是签署完成状态
                return SIGN_STATUS_FINISH.equals(partySign.getStatus());
            }).map(PartySignDO::getAccountId).collect(Collectors.toList());
//            // 有需要发送的账户
//            if (!CollectionUtils.isEmpty(accountList)) {
//                sendSignEndWeiXinTemp(contractDO, accountList, tokenUser);
//            }
        }
        //签署成功回调
        this.contractSignCompleteCallback(contractDO, appendixDO, tokenUser);
    }

    /**
     * 公司根据字节流签署
     *
     * @param contractDO   合同
     * @param accountDO    账号
     * @param partySignDO  签署方
     * @param bytes        字节流
     * @param showDiffSite 合同在不同站点中展示
     * @param tokenUser    当前用户
     */
    private void enterpriseSignByBytes2(ContractDO contractDO, AccountDO accountDO, PartySignDO partySignDO
            , byte[] bytes, Boolean showDiffSite, TokenUser tokenUser) {
        Long contractId = contractDO.getId();
        //从合同分类获取企业印章id
        CategoryDO categoryDO = categoryService.selectDOById(contractDO.getCategoryId(), tokenUser.getSiteId());
        String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
        String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
        MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), bytes);
        MultipartFile[] files = new MultipartFile[1];
        files[0] = file;
        //上传签署后的合同文件
        List<String> pathList;

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (showDiffSite) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }

        //把签署前的文件删除
        AppendixDO originFile = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        log.info("=合同文件,签署前文件DO:{},ID:{}", originFile, originFile.getId());
        if (originFile != null) {
            appendixService.deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), originFile.getId());
        }
        AppendixDO appendixDO = new AppendixDO();
        if (!CollectionUtils.isEmpty(pathList)) {
            appendixDO.setContractId(contractId);
            appendixDO.setCurrentPage(0);
            appendixDO.setPath(pathList.get(0));
            //保存签署后的合同文件
            AppendixDO save = appendixService.save(appendixDO, tokenUser.getSiteId(), tokenUser.getUserId());
            log.info("=合同文件,重新创建后文件DO:{},ID:{}", save, save.getId());
        }
        //更新签署方签署状态
        partySignDO.setSignDate(new Date());
        partySignDO.setStatus(SIGN_STATUS_FINISH);
        partySignService.updateByIdSelective(partySignDO, tokenUser.getSiteId(), tokenUser.getUserId());
        //修改合同状态
        contractDO.setStatus(CONTRACT_STATUS_FINISH);
        // 签署完成时间
        contractDO.setSignFinishDate(new Date());
        // 更新合同状态
        contractService.updateByIdSelective(contractDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 双方签署的合同,合同签署完成后通知用户
        if (SignMethod.BOTH.getValue().equals(categoryDO.getSignType())) {
            List<PartySignDO> partySignList = partySignService.selectByContractId(contractId, tokenUser.getSiteId());
            // 获取除公司签署方外的签署完成的签署方
            List<String> accountList = partySignList.stream().filter(partySign -> {
                // 是公司的签署方
                if (partySign.getId().equals(partySignDO.getId())) {
                    return false;
                }
                // 不是签署完成状态
                return SIGN_STATUS_FINISH.equals(partySign.getStatus());
            }).map(PartySignDO::getAccountId).collect(Collectors.toList());
//            // 有需要发送的账户
//            if (!CollectionUtils.isEmpty(accountList)) {
//                sendSignEndWeiXinTemp(contractDO, accountList, tokenUser);
//            }
        }
        //签署成功回调
        this.contractSignCompleteCallback(contractDO, appendixDO, tokenUser);
    }

//    /**
//     * 异步发送合同签署完成-微信公众号模板消息
//     *
//     * @param contractDO  合同信息
//     * @param accountList 签署的员工账号
//     * @param tokenUser   当前用户
//     */
//    @Async
//    void sendSignEndWeiXinTemp(ContractDO contractDO, List<String> accountList, TokenUser tokenUser) {
//        if (Objects.isNull(contractDO)) {
//            return;
//        }
//        if (StringUtils.isEmpty(appId) || StringUtils.isEmpty(signEndTempId)) {
//            return;
//        }
//        if (CollectionUtils.isEmpty(accountList)) {
//            return;
//        }
//        // 循环获取发送
//        for (String accountId : accountList) {
//            AccountDO accountUsre = accountService.selectDOById(accountId);
//            if (Objects.isNull(accountUsre) || StringUtils.isEmpty(accountUsre.getMemberId())) {
//                continue;
//            }
//            UnionOpenIdDO unionOpenId = userProviderService.getUnionOpenIdByUser(accountUsre.getMemberId(), appId);
//            if (Objects.nonNull(unionOpenId) && StringUtils.isNotEmpty(unionOpenId.getOpenId())) {
//                String openId = unionOpenId.getOpenId();
//                // 推送微信模板消息
//                WeiXinDTO weiXinDTO = new WeiXinDTO();
//                weiXinDTO.setTemplateId(signEndTempId);
//                weiXinDTO.setToUser(openId);
//                // 参数
//                List<WxTemplateParam> paramList = new ArrayList<>();
//                WxTemplateParam param1 = new WxTemplateParam();
//                param1.setName("first");
//                param1.setValue("您好,您的电子合同已签署成功");
//                paramList.add(param1);
//                // 合同签署编号
//                WxTemplateParam param2 = new WxTemplateParam();
//                param2.setName("keyword1");
//                param2.setValue(contractDO.getSignNo());
//                paramList.add(param2);
//                // 甲方名称
//                WxTemplateParam param3 = new WxTemplateParam();
//                param3.setName("keyword2");
//                param3.setValue(contractDO.getPartyAName());
//                paramList.add(param3);
//                // 乙方名称
//                WxTemplateParam param4 = new WxTemplateParam();
//                param4.setName("keyword3");
//                param4.setValue(contractDO.getPartyBName());
//                paramList.add(param4);
//                // 签署时间
//                WxTemplateParam param5 = new WxTemplateParam();
//                param5.setName("keyword4");
//                param5.setValue(DateKit.getDateTimeStr(new Date()));
//                paramList.add(param5);
////
////                WxTemplateParam param7 = new WxTemplateParam();
////                param7.setName("remark");
////                param7.setValue("请仔细查看电子合同文件");
////                paramList.add(param7);
//
//                weiXinDTO.setTemplateParamList(paramList);
//                // 跳转小程序
//                WxMiNiParam wxMiNiParam = new WxMiNiParam();
//                wxMiNiParam.setAppid(miNiAppId);
//                wxMiNiParam.setPagepath(miNiPagePath);
//                weiXinDTO.setMiniProgram(wxMiNiParam);
//                messageProviderService.sendWeChatTempMessage(weiXinDTO, tokenUser);
//            }
//        }
//    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void personAndEnterpriseSign(Long contractId, String companyId, String sealString, String code, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        // 将个人和企业的章打到合同上
        personAndEnterpriseSignBySite(contractId, companyId, code, sealString, Boolean.FALSE, tokenUser);
        // 将个人签名保存起来
        personSignatureService.pubshPersonSignature(contractId, sealString, tokenUser);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void personAndEnterpriseSignDiffSite(Long contractId, String companyId, String sealString, String code, TokenUser tokenUser) {
        ParamException.notNull(contractId, "合同id不能为空");
        // 将个人和企业的章打到合同上
        personAndEnterpriseSignBySite(contractId, companyId, code, sealString, Boolean.TRUE, tokenUser);
        // 将个人签名保存起来
        personSignatureService.pubshPersonSignature(contractId, sealString, tokenUser);
    }

    /**
     * 个人和公司签署合同,个人签署完成后,公司默认自动签署
     *
     * @param contractId   合同id
     * @param companyId    公司id
     * @param code         个人合同验证码
     * @param sealPerson   个人前面
     * @param showDiffSite 签署后是否是不同站点显示合同
     * @param tokenUser    当前用户
     */
    private void personAndEnterpriseSignBySite(Long contractId, String companyId, String code, String sealPerson, Boolean showDiffSite, TokenUser tokenUser) {

        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在或已删除");

        //获取签署人账户对象
        AccountDO accountPerson = accountService.findAccount(AccountType.PERSON, tokenUser.getUserId());
        ParamException.notNull(accountPerson, "用户未实名");
        ParamException.isTrue(accountPerson.getReal(), "用户未实名");

        //获取账户对象
        AccountDO accountCompany = accountService.findAccount(AccountType.ENTERPRISE, companyId);
        ParamException.notNull(accountCompany, "企业未认证");
        ParamException.isTrue(accountCompany.getReal(), "企业未认证");

        //获取个人签署方
        PartySignDO partySignPerson = partySignService.selectByContractParty(contractId, accountPerson.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignPerson, "未找到个人合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignPerson.getStatus()), "合同已过期");

        //获取公司签署方
        PartySignDO partySignCompany = partySignService.selectByContractParty(contractId, accountCompany.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignCompany, "未找到企业合同签署方");

        //调用消息接口验证验证码
        Boolean result = this.checkCodeProvider(accountPerson.getMobile(), code, tokenUser);
        //验证成功
        if (!result) {
            throw new ParamException("验证码不正确");
        }

        //获取签署文件字节流
        byte[] bytes = this.getContractFileStream(contractDO, showDiffSite, tokenUser);

        //获取个人签署位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        if (contractDO.getTemplateId() != null) {
            sealPositionQuery.setTemplateId(contractDO.getTemplateId());
        }
        sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
        List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        ParamException.notEmpty(sealPositionList, "合同尚未设置签署位置");
        // 个人签署位置
        List<SealPositionDO> sealPositionPersonList = sealPositionList.stream()
                .filter(seal -> AccountType.PERSON.getValue().equals(seal.getAccountType()))
                .collect(Collectors.toList());
        ParamException.notEmpty(sealPositionPersonList, "合同尚未设置个人签署位置");
        // 公司签署位置
        List<SealPositionDO> sealPositionCompanyList = sealPositionList.stream()
                .filter(seal -> AccountType.ENTERPRISE.getValue().equals(seal.getAccountType()))
                .collect(Collectors.toList());
        ParamException.notEmpty(sealPositionPersonList, "合同尚未设置公司签署位置");

        //调用第三方进行个人签署
        FileDigestSignResult personFileDigestSignResult = thirdSignService.localSafeSignPDF(accountPerson.getAccountId()
                , sealPerson, bytes, sealPositionPersonList, SealType.SINGLE, tokenUser);
        byte[] resultPersonStream = personFileDigestSignResult.getStream();
        log.info("合同日志追踪===>个人签署然后公司自动签署，个人签署调用第三方签署成功，userId:{},accountId:{},name:{},合同md5:{}",
                tokenUser.getUserId(), accountPerson.getAccountId(), accountPerson.getName(),
                DigestUtils.md5DigestAsHex(resultPersonStream));
        partySignPerson.setSignServiceId(personFileDigestSignResult.getSignServiceId());
        partySignPerson.setSignDetailUrl(personFileDigestSignResult.getSignDetailUrl());
        // 从合同分类获取企业印章id
        CategoryDO categoryCompany = categoryService.selectDOById(contractDO.getCategoryId(), tokenUser.getSiteId());
        Long sealId = categoryCompany.getEnterpriseSealId();
        SealDO sealDO = sealService.selectDOById(sealId, tokenUser.getSiteId());
        String sealCompany = sealDO.getSealString();

        // 调用第三方进行公司签署
        FileDigestSignResult CompanyFileDigestSignResult = thirdSignService.localSafeSignPDF(accountCompany.getAccountId()
                , sealCompany, resultPersonStream, sealPositionCompanyList, SealType.SINGLE, tokenUser);
        byte[] resultCompanyStream = CompanyFileDigestSignResult.getStream();
        log.info("合同日志追踪===>个人签署然后公司自动签署，公司签署调用第三方签署成功，userId:{},accountId:{},name:{},合同md5:{}",
                tokenUser.getUserId(), accountCompany.getAccountId(), accountCompany.getName(),
                DigestUtils.md5DigestAsHex(resultCompanyStream));
        partySignCompany.setSignServiceId(CompanyFileDigestSignResult.getSignServiceId());
        partySignCompany.setSignDetailUrl(CompanyFileDigestSignResult.getSignDetailUrl());
        String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
        String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
        MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), resultCompanyStream);
        MultipartFile[] files = new MultipartFile[1];
        files[0] = file;
        //上传签署后的合同文件
        List<String> pathList;

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (showDiffSite) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }

        //把签署前的文件删除
        AppendixDO originFile = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        if (originFile != null) {
            appendixService.deleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), originFile.getId());
        }
        AppendixDO appendixDO = new AppendixDO();
        if (!CollectionUtils.isEmpty(pathList)) {
            String path = pathList.get(0);
            appendixDO.setContractId(contractId);
            appendixDO.setCurrentPage(0);
            appendixDO.setPath(path);
            //保存签署后的合同文件
            appendixService.save(appendixDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        //修改个人签署方状态
        partySignPerson.setSignDate(new Date());
        partySignPerson.setStatus(SIGN_STATUS_FINISH);
        partySignService.updateByIdSelective(partySignPerson, tokenUser.getSiteId(), tokenUser.getUserId());

        //更新企业签署方签署状态
        partySignCompany.setSignDate(new Date());
        partySignCompany.setStatus(SIGN_STATUS_FINISH);
        partySignService.updateByIdSelective(partySignCompany, tokenUser.getSiteId(), tokenUser.getUserId());

        //修改合同状态,签署完毕
        contractDO.setStatus(CONTRACT_STATUS_FINISH);
        // 签署完成时间
        contractDO.setSignFinishDate(new Date());
        contractService.updateByIdSelective(contractDO, tokenUser.getSiteId(), tokenUser.getUserId());
        //签署成功回调
        this.contractSignCompleteCallback(contractDO, appendixDO, tokenUser);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void publishTemplateMultiSign(Long templateId, Long categoryId, String contractName, List<String> enterprisePartyAccountIdList,
                                         List<String> personalPartyAccountIdList, TokenUser tokenUser) {
        ParamException.notNull(templateId, "模板id不能为空");
        ParamException.notNull(categoryId, "分类id不能为空");
        ParamException.notNull(contractName, "合同名称不能为空");
        CategoryDO categoryDO = categoryService.selectDOById(categoryId, tokenUser.getSiteId());
        ParamException.notNull(categoryDO, "合同分类不存在");
        ContractAddDTO contractAddDTO = new ContractAddDTO();
        contractAddDTO.setCategoryId(categoryId);
        contractAddDTO.setTemplateId(templateId);
        contractAddDTO.setName(contractName);
        contractAddDTO.setContractType(ContractType.ONLINE.getValue());
        contractAddDTO.setFileType(categoryDO.getFileType());
        //保存合同表
        ContractVO contractVO = contractService.save(contractAddDTO, tokenUser);
        //查询模板签章位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        sealPositionQuery.setTemplateId(templateId);
        sealPositionQuery.setType(ContractConstants.SEAL_POSITION_TYPE_REGULAR);
        List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(sealPositionList)) {
            throw new ParamException("请先设置模板默认印章位置");
        }
        if ((enterprisePartyAccountIdList == null ? 0 : enterprisePartyAccountIdList.size()) +
                (personalPartyAccountIdList == null ? 0 : personalPartyAccountIdList.size()) > PARTY_MAX) {
            throw new ParamException("暂不支持两方以上签订合同");
        }
        //双方签署或仅企业签署
        if (SignMethod.BOTH.getValue().equals(categoryDO.getSignType()) || SignMethod.ENTERPRISE_ONLY.getValue().equals(categoryDO.getSignType())) {
            ParamException.notNull(enterprisePartyAccountIdList, "企业方id列表不能为空");
            //保存企业签署方
            for (String accountId : enterprisePartyAccountIdList) {
                PartySignAddDTO partySignAddDTO = new PartySignAddDTO();
                partySignAddDTO.setAccountId(accountId);
                partySignAddDTO.setContractId(contractVO.getId());
                SealPositionDO enterpriseSealPosition = sealPositionList.stream()
                        .filter(sealPositionDO -> AccountType.ENTERPRISE.getValue().equals(sealPositionDO.getAccountType()))
                        .findFirst().orElseThrow(() -> new ParamException("未设置企业默认印章位置"));
                partySignAddDTO.setSealPositionId(enterpriseSealPosition.getId());
                partySignAddDTO.setSealId(categoryDO.getEnterpriseSealId());
                partySignAddDTO.setStatus(SIGN_STATUS_READY);
                partySignService.save(partySignAddDTO, tokenUser);
            }
        }
        //双方签署或仅员工签署
        if (SignMethod.BOTH.getValue().equals(categoryDO.getSignType()) || SignMethod.PERSON_ONLY.getValue().equals(categoryDO.getSignType())) {
            ParamException.notNull(personalPartyAccountIdList, "个人方id列表不能为空");
            //保存员工签署方
            for (String accountId : personalPartyAccountIdList) {
                PartySignAddDTO partySignAddDTO = new PartySignAddDTO();
                partySignAddDTO.setAccountId(accountId);
                partySignAddDTO.setContractId(contractVO.getId());
                SealPositionDO personSealPosition = sealPositionList.stream()
                        .filter(sealPositionDO -> AccountType.PERSON.getValue().equals(sealPositionDO.getAccountType()))
                        .findFirst().orElseThrow(() -> new ParamException("未设置个人默认印章位置"));
                partySignAddDTO.setSealPositionId(personSealPosition.getId());
                partySignAddDTO.setStatus(SIGN_STATUS_READY);
                partySignService.save(partySignAddDTO, tokenUser);
            }
        }
        //保存合同模板变量表
        List<VariableDO> variableList = variableService.selectDO(new VariableQuery());
        List<ContractVariableAddDTO> contractVariableList = new ArrayList<>();
        for (VariableDO variableDO : variableList) {
            ContractVariableAddDTO contractVariableAddDTO = new ContractVariableAddDTO();
            contractVariableAddDTO.setVariableId(variableDO.getId());
            contractVariableAddDTO.setContractId(contractVO.getId());
            contractVariableList.add(contractVariableAddDTO);
        }
        contractVariableService.batchSave(contractVariableList, tokenUser);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void wordSignCompany(PublishWordCompanySignDTO publishWordCompanySignDTO, TokenUser tokenUser) {
        ParamException.notNull(publishWordCompanySignDTO.getContractId(), "合同id不能为空");
        //获取账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.ENTERPRISE, tokenUser.getSiteId());
        ParamException.notNull(accountDO, "企业未认证");
        ParamException.isTrue(accountDO.getReal(), "企业未认证");
        //获取签署方
        PartySignDO partySignDO = partySignService.selectByContractParty(publishWordCompanySignDTO.getContractId(), accountDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignDO, "未找到合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignDO.getStatus()), "合同状态不正确");
        //获取合同对象
        ContractDO contractDO = contractService.selectDOById(publishWordCompanySignDTO.getContractId(), tokenUser.getSiteId());
        ParamException.notNull(contractDO, "合同不存在");
        // 获取签署对象中的印章对象
        SealDO sealDO = sealService.selectDOById(partySignDO.getSealId(), tokenUser.getSiteId());
        ParamException.notNull(sealDO, "无法找到印章，请联系管理员或重新发起合同");
        // 获取合同类型对象
        CategoryDO categoryDO = categoryService.selectDOById(contractDO.getCategoryId(), tokenUser.getSiteId());
        ParamException.notNull(categoryDO, "无法找到该合同类型，请联系管理员或重新发起合同");
        //获取待签署文件字节流
        byte[] bytes = this.getContractFileStream(contractDO, publishWordCompanySignDTO.getShowDiffSite(), tokenUser);

        // 根据字节流进行签署
        // 根据模板id获取签署位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        sealPositionQuery.setTemplateId(contractDO.getTemplateId());
        sealPositionQuery.setAccountType(AccountType.ENTERPRISE.getValue());
        sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
        List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        ParamException.isFalse(CollectionUtils.isEmpty(sealPositionList), "没有找到该模板的默认签署位置，请重新设置该合同模板");
        byte[] resultStream = bytes;
        String signServiceId = null;
        String signDetailUrl = null;
        for (SealPositionDO sealPositionDO : sealPositionList) {
            //调用第三方进行签署
            List<SealPositionDO> sealPositionDOList = new ArrayList<>();
            sealPositionDOList.add(sealPositionDO);
            FileDigestSignResult fileDigestSignResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                    , sealDO.getSealString(), resultStream, sealPositionDOList, SealType.SINGLE, tokenUser);
            resultStream = fileDigestSignResult.getStream();
            signServiceId = fileDigestSignResult.getSignServiceId();
            signDetailUrl = fileDigestSignResult.getSignDetailUrl();
            log.info("合同日志追踪===>根据word模板企业签署，调用第三方签署成功，印章位置：{}, userId:{},accountId:{},name:{},合同md5:{}",
                    sealPositionDO.getSealPositionX(),
                    tokenUser.getUserId(), accountDO.getAccountId(), accountDO.getName(),
                    DigestUtils.md5DigestAsHex(resultStream));
        }
        // 是否有法人章需要签署
        // 根据模板id及法人签章类型获取签署位置
        sealPositionQuery = new SealPositionQuery();
        sealPositionQuery.setTemplateId(contractDO.getTemplateId());
        sealPositionQuery.setAccountType(AccountType.LEGAL_PERSON.getValue());
        sealPositionQuery.setType(LEGAL_PERSON);
        sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(sealPositionList)) {
            if (sealDO.getIsLegalPerson() == null || !sealDO.getIsLegalPerson()) {
                throw new ParamException("签署法人章需要在印章中设置法人章相关信息,请先前往设置！");
            }
            //调用第三方进行签署
            List<SealPositionDO> sealPositionDOList = new ArrayList<>();
            sealPositionDOList.add(sealPositionList.get(0));

            FileDigestSignResult fileDigestSignResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                    , sealDO.getLegalPersonPicture(), resultStream, sealPositionDOList, SealType.SINGLE, tokenUser);
            resultStream = fileDigestSignResult.getStream();
            signServiceId = fileDigestSignResult.getSignServiceId();
            signDetailUrl = fileDigestSignResult.getSignDetailUrl();
            log.info("合同日志追踪===>根据word模板企业签署(法人)，调用第三方签署成功，印章位置：{}, userId:{},accountId:{},name:{},合同md5:{}",
                    sealPositionList.get(0).getSealPositionX(),
                    tokenUser.getUserId(), accountDO.getAccountId(), accountDO.getName(),
                    DigestUtils.md5DigestAsHex(resultStream));
        }
        String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());
        String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
        MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), resultStream);
        MultipartFile[] files = new MultipartFile[1];
        files[0] = file;
        //上传签署后的合同文件
        List<String> pathList;

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (publishWordCompanySignDTO.getShowDiffSite() != null && publishWordCompanySignDTO.getShowDiffSite()) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }

        //修改合同文件
        AppendixDO originFile = appendixService.selectByContractId(contractDO.getId(), tokenUser.getSiteId());
        if (originFile != null) {
            AppendixUpdateDTO appendixUpdateDTO = new AppendixUpdateDTO();
            if (!CollectionUtils.isEmpty(pathList)) {
                String path = pathList.get(0);
                appendixUpdateDTO.setId(originFile.getId());
                appendixUpdateDTO.setContractId(contractDO.getId());
                appendixUpdateDTO.setCurrentPage(0);
                appendixUpdateDTO.setPath(path);
                //修改签署后的合同文件
                appendixService.update(appendixUpdateDTO, tokenUser);
            }
        } else {
            throw new ParamException("合同附件为空，请联系管理员或重新发起合同");
        }
        originFile = appendixService.selectByContractId(contractDO.getId(), tokenUser.getSiteId());

        //更新签署方签署状态
        partySignDO.setSignServiceId(signServiceId);
        partySignDO.setSignDetailUrl(signDetailUrl);
        partySignDO.setSignDate(new Date());
        partySignDO.setStatus(SIGN_STATUS_FINISH);
        partySignService.updateByIdSelective(partySignDO, tokenUser.getSiteId(), tokenUser.getUserId());
        //修改合同状态
        contractDO.setStatus(CONTRACT_STATUS_FINISH);
        // 签署完成时间
        contractDO.setSignFinishDate(new Date());
        // 更新合同状态
        contractService.updateByIdSelective(contractDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 双方签署的合同,合同签署完成后通知用户
        if (SignMethod.BOTH.getValue().equals(categoryDO.getSignType())) {
            List<PartySignDO> partySignList = partySignService.selectByContractId(publishWordCompanySignDTO.getContractId(), tokenUser.getSiteId());
            // 获取除公司签署方外的签署完成的签署方
            List<String> accountList = partySignList.stream().filter(partySign -> {
                // 是公司的签署方
                if (partySign.getId().equals(partySignDO.getId())) {
                    return false;
                }
                // 不是签署完成状态
                return SIGN_STATUS_FINISH.equals(partySign.getStatus());
            }).map(PartySignDO::getAccountId).collect(Collectors.toList());
//            // 有需要发送的账户
//            if (!CollectionUtils.isEmpty(accountList)) {
//                sendSignEndWeiXinTemp(contractDO, accountList, tokenUser);
//            }
        }
        //签署成功回调
        this.contractSignCompleteCallback(contractDO, originFile, tokenUser);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public void wordSignPeople(PublishWordPeopleSignDTO publishWordPeopleSignDTO, TokenUser tokenUser) {
        tokenUser.setSiteId(publishWordPeopleSignDTO.getSiteId());
        // 获取合同
        ContractDO contractDO = contractService.selectDOById(publishWordPeopleSignDTO.getContractId(), tokenUser.getSiteId());
        // 获取签署人账户对象
        AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUser.getUserId());
        ParamException.notNull(accountDO, "用户未实名");
        ParamException.isTrue(accountDO.getReal(), "用户未实名");
        //调用消息接口验证验证码
        Boolean result = this.checkCodeProvider(accountDO.getMobile(), publishWordPeopleSignDTO.getCode(), tokenUser);
        //验证成功
        if (!result) {
            throw new ParamException("验证码不正确");
        }
        // 获取签署方对象
        PartySignDO partySignDO = partySignService.selectByContractParty(publishWordPeopleSignDTO.getContractId(), accountDO.getId(), tokenUser.getSiteId());
        ParamException.notNull(partySignDO, "未找到合同签署方");
        ParamException.isTrue(SIGN_STATUS_READY.equals(partySignDO.getStatus()), "合同已过期");
        // 获取文件字节流
        byte[] bytes = getContractFileStream(contractDO, publishWordPeopleSignDTO.getShowDiffSite(), tokenUser);
        Long contractId = contractDO.getId();
        // 根据模板id获取签署位置
        SealPositionQuery sealPositionQuery = new SealPositionQuery();
        sealPositionQuery.setTemplateId(contractDO.getTemplateId());
        sealPositionQuery.setAccountType(AccountType.PERSON.getValue());
        sealPositionQuery.setType(SEAL_POSITION_TYPE_REGULAR);
        List<SealPositionDO> sealPositionList = sealPositionService.selectDO(sealPositionQuery, tokenUser.getSiteId());
        ParamException.isFalse(CollectionUtils.isEmpty(sealPositionList), "没有找到该模板的默认签署位置，请重新设置该合同模板");
        byte[] resultStream = bytes;
        String signServiceId = null;
        String signDetailUrl = null;
        for (SealPositionDO sealPositionDO : sealPositionList) {
            //调用第三方进行签署
            List<SealPositionDO> sealPositionDOList = new ArrayList<>();
            sealPositionDOList.add(sealPositionDO);
            FileDigestSignResult fileDigestSignResult = thirdSignService.localSafeSignPDF(accountDO.getAccountId()
                    , publishWordPeopleSignDTO.getSealString(), resultStream,
                    sealPositionDOList, SealType.SINGLE, tokenUser);
            resultStream = fileDigestSignResult.getStream();
            signServiceId = fileDigestSignResult.getSignServiceId();
            signDetailUrl = fileDigestSignResult.getSignDetailUrl();
            log.info("合同日志追踪===>根据word模板个人签署，调用第三方签署成功，印章位置：{}, userId:{},accountId:{},name:{},合同md5:{}",
                    sealPositionDO.getSealPositionX(),
                    tokenUser.getUserId(), accountDO.getAccountId(), accountDO.getName(),
                    DigestUtils.md5DigestAsHex(resultStream));
        }
        String fileName = contractPathService.getFileName(contractDO, tokenUser.getSiteId());

        String contractPdfName = contractPathService.getOriginalFilename(contractDO, tokenUser.getSiteId());
        MultipartFile file = new MockMultipartFile(fileName, contractPdfName, ContentType.APPLICATION_OCTET_STREAM.toString(), resultStream);
        MultipartFile[] files = new MultipartFile[1];
        files[0] = file;
        //上传签署后的合同文件
        List<String> pathList;

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (publishWordPeopleSignDTO.getShowDiffSite() != null && publishWordPeopleSignDTO.getShowDiffSite()) {
            tokenUserSite.setSiteId("defaultSite");
            // 不同站点中使用
            pathList = siteFileService.uploadDiffSite(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        } else {
            // 相同站点中使用
            pathList = siteFileService.upload(files, FileConstants.DOWNLOAD_TYPE_INLINE, tokenUserSite);
        }

        //修改合同文件
        AppendixDO originFile = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        if (originFile != null) {
            AppendixUpdateDTO appendixUpdateDTO = new AppendixUpdateDTO();
            if (!CollectionUtils.isEmpty(pathList)) {
                String path = pathList.get(0);
                appendixUpdateDTO.setId(originFile.getId());
                appendixUpdateDTO.setContractId(contractId);
                appendixUpdateDTO.setCurrentPage(0);
                appendixUpdateDTO.setPath(path);
                //保存签署后的合同文件
                appendixService.update(appendixUpdateDTO, tokenUser);
            }
        } else {
            throw new ParamException("合同附件为空，请联系管理员或重新发起合同");
        }
        originFile = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        //修改签署方状态
        partySignDO.setSignServiceId(signServiceId);
        partySignDO.setSignDetailUrl(signDetailUrl);
        partySignDO.setSignDate(new Date());
        partySignDO.setStatus(SIGN_STATUS_FINISH);
        partySignService.updateByIdSelective(partySignDO, tokenUser.getSiteId(), tokenUser.getUserId());
        //修改合同状态
        CategoryDO categoryDO = categoryService.selectDOById(contractDO.getCategoryId(), tokenUser.getSiteId());
        // 判断合同名称是否包含单方签署
        if (contractDO.getName().contains("单方签署")) {
            //签署完毕
            contractDO.setStatus(CONTRACT_STATUS_FINISH);
            // 签署完成时间
            contractDO.setSignFinishDate(new Date());
        } else {
            //待企业签署
            contractDO.setStatus(CONTRACT_STATUS_ENTERPRISE);
        }
        //签署成功回调
        this.contractSignSuccessCallback(contractDO, originFile, tokenUser);
        // 员工签署时间使用当前日期
        contractDO.setEmpSignDate(new Date());
        contractService.updateByIdSelective(contractDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    /**
     * 获取合同文件流
     *
     * @param contractDO 合同对象
     * @return 字节流
     */
    private byte[] getContractFileStream(ContractDO contractDO, Boolean showDiffSite, TokenUser tokenUser) {
        AppendixDO appendixDO = appendixService.selectByContractId(contractDO.getId(), contractDO.getSiteId());
        String path = appendixDO.getPath();

        TokenUser tokenUserSite = new TokenUser();
        tokenUserSite.setSiteId(tokenUser.getSiteId());
        tokenUserSite.setUserId(tokenUser.getUserId());
        if (showDiffSite) {
            tokenUserSite.setSiteId("defaultSite");
        }
        FileVersionDO fileVersionDO = siteFileService.download(path, tokenUserSite);
        return FileHelper.getFileBytes(fileVersionDO.getTempFilePath());
    }

    /**
     * 保存合同文件
     *
     * @param contractId 合同id
     * @param path       路径
     * @param tokenUser  当前登录用户
     */
    private void saveAppendix(Long contractId, String path, TokenUser tokenUser) {
        AppendixDO appendixDO = new AppendixDO();
        appendixDO.setContractId(contractId);
        appendixDO.setCurrentPage(0);
        appendixDO.setPath(path);
        //保存签署后的合同文件
        appendixService.save(appendixDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    /**
     * 调用通用服务验证验证码
     *
     * @return result对象
     */
    private Boolean checkCodeProvider(String phone, String code, TokenUser tokenUser) {
        ShortMessageDTO shortMessageDTO = new ShortMessageDTO();
        shortMessageDTO.setAppName(appName);
        shortMessageDTO.setCaptcha(code);
        shortMessageDTO.setScene(SCENE);
        shortMessageDTO.setPhone(phone);
        return messageProviderService.verifyCaptcha(shortMessageDTO, tokenUser);
    }

    /**
     * 回调各模块的合同签署成功处理
     *
     * @param contractDO 合同对象
     * @param appendixDO 合同附件
     * @param tokenUser  当前登录用户
     */
    @Async
    void contractSignSuccessCallback(ContractDO contractDO, AppendixDO appendixDO, TokenUser tokenUser) {
        //获取签署方账户信息
        List<PartySignAccountVO> partySignAccountVOList = partySignService.selectAccountByContractId(contractDO.getId(), tokenUser.getSiteId());
        for (IContractSignCallbackService contractSignCallbackService : contractSignCallbackServiceList) {
            contractSignCallbackService.onSuccess(contractDO, partySignAccountVOList, appendixDO, tokenUser);
        }
    }

    /**
     * 回调各模块的合同签署完成处理
     *
     * @param contractDO 合同对象
     * @param appendixDO 合同附件
     * @param tokenUser  当前登录用户
     */
    @Async
    void contractSignCompleteCallback(ContractDO contractDO, AppendixDO appendixDO, TokenUser tokenUser) {
        //获取签署方账户信息
        List<PartySignAccountVO> partySignAccountVOList = partySignService.selectAccountByContractId(contractDO.getId(), tokenUser.getSiteId());
        for (IContractSignCallbackService contractSignCallbackService : contractSignCallbackServiceList) {
            contractSignCallbackService.onComplete(contractDO, partySignAccountVOList, appendixDO, tokenUser);
        }
    }

    /**
     * 回调各模块的发起合同逻辑
     *
     * @param contractIdList 合同id列表
     * @param tokenUser      当前登录用户
     */
    @Async
    void contractPublishCallback(List<Long> contractIdList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(contractIdList)) {
            return;
        }
        for (Long contractId : contractIdList) {
            //合同对象
            ContractDO contractDO = contractService.selectDOById(contractId, tokenUser.getSiteId());
            //获取签署方账户信息
            List<PartySignAccountVO> partySignAccountVOList = partySignService.selectAccountByContractId(contractDO.getId(), tokenUser.getSiteId());
            //合同附件
            AppendixDO appendixDO = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
            //回调
            for (IContractSignCallbackService contractSignCallbackService : contractSignCallbackServiceList) {
                contractSignCallbackService.onPublish(contractDO, partySignAccountVOList, appendixDO, tokenUser);
            }
        }
    }

    /**
     * 发通知回调
     *
     * @param sendList  信息参数
     * @param tokenUser 当前用户
     */
    @Async
    void sendMessageCallback(List<SignWeiXinSendDTO> sendList, String subjectName, TokenUser tokenUser) {
        for (IContractSignCallbackService contractSignCallbackService : contractSignCallbackServiceList) {
            contractSignCallbackService.sendMessage(sendList, subjectName, tokenUser);
        }
    }
}
